From 1b753060b3622336689ae2b6789cc959f037b32b Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Tue, 26 Sep 2023 10:36:00 -0700 Subject: [PATCH] Responsive home page and use cases ui (#2468) ![Kapture 2023-09-21 at 15 51 53](https://github.com/microsoft/typespec/assets/1031227/10f2b416-ec9b-43ac-b340-d10abeb9f798) image --- eng/pipelines/pr-tryit.yml | 1 + .../src/components/card/card.module.css | 5 ++ packages/website/src/components/card/card.tsx | 11 +++ .../description-text/secondary-text.tsx | 14 ++++ .../website/src/components/fluent-layout.tsx | 19 ++++- .../homepage/feature/feature.module.css | 52 +++++++++++++ .../components/homepage/feature/feature.tsx | 27 +++++++ .../components/homepage/homepage.module.css | 39 ++-------- .../src/components/homepage/homepage.tsx | 48 +++--------- .../homepage/section/section.module.css | 38 +++++++++- .../components/homepage/section/section.tsx | 55 ++++++++------ .../sectioned-layout.module.css | 10 +++ .../sectioned-layout/sectioned-layout.tsx | 5 ++ .../use-case-feature.module.css | 49 ++++++++++++ .../use-case-feature/use-case-feature.tsx | 26 +++++++ .../use-case-overview.module.css | 64 ++++++++++++++++ .../use-case-overview/use-case-overview.tsx | 32 ++++++++ .../website/src/pages/data-validation.tsx | 76 ++++++++++++++++++- packages/website/src/pages/json-rpc.tsx | 76 ++++++++++++++++++- packages/website/src/pages/openapi.tsx | 76 ++++++++++++++++++- 20 files changed, 623 insertions(+), 100 deletions(-) create mode 100644 packages/website/src/components/card/card.module.css create mode 100644 packages/website/src/components/card/card.tsx create mode 100644 packages/website/src/components/description-text/secondary-text.tsx create mode 100644 packages/website/src/components/homepage/feature/feature.module.css create mode 100644 packages/website/src/components/homepage/feature/feature.tsx create mode 100644 packages/website/src/components/sectioned-layout/sectioned-layout.module.css create mode 100644 packages/website/src/components/sectioned-layout/sectioned-layout.tsx create mode 100644 packages/website/src/components/use-case-feature/use-case-feature.module.css create mode 100644 packages/website/src/components/use-case-feature/use-case-feature.tsx create mode 100644 packages/website/src/components/use-case-overview/use-case-overview.module.css create mode 100644 packages/website/src/components/use-case-overview/use-case-overview.tsx diff --git a/eng/pipelines/pr-tryit.yml b/eng/pipelines/pr-tryit.yml index 76593f6003..4f92952721 100644 --- a/eng/pipelines/pr-tryit.yml +++ b/eng/pipelines/pr-tryit.yml @@ -2,6 +2,7 @@ trigger: none pr: - main - release/* + - feature/website-design jobs: - job: publish_playground diff --git a/packages/website/src/components/card/card.module.css b/packages/website/src/components/card/card.module.css new file mode 100644 index 0000000000..306ad26336 --- /dev/null +++ b/packages/website/src/components/card/card.module.css @@ -0,0 +1,5 @@ +.card { + background-color: var(--colorNeutralBackground1); + border-radius: 22px; + padding: 20px; +} diff --git a/packages/website/src/components/card/card.tsx b/packages/website/src/components/card/card.tsx new file mode 100644 index 0000000000..309dc7c5db --- /dev/null +++ b/packages/website/src/components/card/card.tsx @@ -0,0 +1,11 @@ +import { mergeClasses } from "@fluentui/react-components"; +import style from "./card.module.css"; + +export interface CardProps { + children?: React.ReactNode; + className?: string; +} + +export const Card = ({ children, className }: CardProps) => { + return
{children}
; +}; diff --git a/packages/website/src/components/description-text/secondary-text.tsx b/packages/website/src/components/description-text/secondary-text.tsx new file mode 100644 index 0000000000..aa99f772d9 --- /dev/null +++ b/packages/website/src/components/description-text/secondary-text.tsx @@ -0,0 +1,14 @@ +import { makeStyles, mergeClasses, tokens } from "@fluentui/react-components"; + +const useFluentStyles = makeStyles({ + descriptionText: { color: tokens.colorNeutralForeground3 }, +}); + +export interface DescriptionTextProps { + children: React.ReactNode; + className?: string; +} +export const DescriptionText = ({ children, className }: DescriptionTextProps) => { + const fluentStyles = useFluentStyles(); + return
{children}
; +}; diff --git a/packages/website/src/components/fluent-layout.tsx b/packages/website/src/components/fluent-layout.tsx index 9735ea015f..2f0405aefc 100644 --- a/packages/website/src/components/fluent-layout.tsx +++ b/packages/website/src/components/fluent-layout.tsx @@ -1,6 +1,12 @@ import BrowserOnly from "@docusaurus/BrowserOnly"; import { useColorMode } from "@docusaurus/theme-common"; -import { FluentProvider, webDarkTheme, webLightTheme } from "@fluentui/react-components"; +import { + FluentProvider, + makeStyles, + tokens, + webDarkTheme, + webLightTheme, +} from "@fluentui/react-components"; import Layout from "@theme/Layout"; export const FluentLayout = ({ children }) => { @@ -18,9 +24,18 @@ const FluentWrapper = ({ children }) => { {() => ( - {children} + {children} )} ); }; + +const useFluentStyles = makeStyles({ + bg: { backgroundColor: tokens.colorNeutralBackground3 }, +}); + +const FluentContainer = ({ children }) => { + const fluentStyles = useFluentStyles(); + return
{children}
; +}; diff --git a/packages/website/src/components/homepage/feature/feature.module.css b/packages/website/src/components/homepage/feature/feature.module.css new file mode 100644 index 0000000000..1dd9997b88 --- /dev/null +++ b/packages/website/src/components/homepage/feature/feature.module.css @@ -0,0 +1,52 @@ +.feature-group { + display: flex; + align-items: stretch; + align-self: center; + flex-direction: column; + gap: 40px; + padding: 80px; +} + +.feature-card { + display: flex; + flex-direction: column; + align-items: center; + gap: 24px; +} + +.feature { + display: flex; + flex-direction: column; + align-items: center; + gap: 24px; +} + +.content { + display: flex; + flex-direction: column; + align-items: center; + gap: 24px; +} + +@media only screen and (min-width: 1200px) { + .feature-group { + flex-direction: row; + width: 1200px; + justify-content: space-between; + padding: 0; + } + + .feature-card { + width: 350px; + } +} + +@media only screen and (min-width: 600px) and (max-width: 1200px) { + .feature { + flex-direction: row; + } + + .content { + align-items: baseline; + } +} diff --git a/packages/website/src/components/homepage/feature/feature.tsx b/packages/website/src/components/homepage/feature/feature.tsx new file mode 100644 index 0000000000..2a2f186b91 --- /dev/null +++ b/packages/website/src/components/homepage/feature/feature.tsx @@ -0,0 +1,27 @@ +import { Subtitle1, Text } from "@fluentui/react-components"; +import { Card } from "../../card/card"; +import { FluentImageName, FluentImg } from "../../fluent-img"; +import style from "./feature.module.css"; + +export interface FeatureProps { + title: string; + image: FluentImageName; + children: React.ReactNode; +} +export const Feature = ({ title, image, children }: FeatureProps) => { + return ( + +
+ +
+ {title} + {children} +
+
+
+ ); +}; + +export const FeatureGroup = ({ children }) => { + return
{children}
; +}; diff --git a/packages/website/src/components/homepage/homepage.module.css b/packages/website/src/components/homepage/homepage.module.css index 48187fe6da..c378b494e1 100644 --- a/packages/website/src/components/homepage/homepage.module.css +++ b/packages/website/src/components/homepage/homepage.module.css @@ -38,7 +38,7 @@ .intro-subtitle { font-size: 20px; - width: 420px; + max-width: 420px; text-align: center; } @@ -52,18 +52,17 @@ align-self: stretch; } +@media only screen and (max-width: 728px) { + .intro-demo { + display: none; + } +} + .intro-demo-image { width: 800px; height: 534px; } -.sections { - display: flex; - padding: 100px 0px 0 0px; - flex-direction: column; - gap: 128px; -} - .overview { display: flex; flex-direction: column; @@ -79,31 +78,10 @@ } .overview-description { - width: 620px; + max-width: 620px; text-align: center; } -.overview-points { - display: flex; - padding: 0px 120px; - justify-content: space-between; - align-items: center; - align-self: stretch; - gap: 88px; -} - -.feature { - display: flex; - flex-direction: column; - align-items: center; - gap: 24px; -} - -.feature > img { - width: 128px; - height: 128px; -} - :global(html[data-theme="dark"]) .closing { background-image: url(/img/bg-bottom-dark.svg); } @@ -120,5 +98,4 @@ gap: 16px; flex: 1 0 0; margin: auto; - width: 1280px; } diff --git a/packages/website/src/components/homepage/homepage.tsx b/packages/website/src/components/homepage/homepage.tsx index cc2e8f8401..f20437350b 100644 --- a/packages/website/src/components/homepage/homepage.tsx +++ b/packages/website/src/components/homepage/homepage.tsx @@ -1,15 +1,8 @@ import useBaseUrl from "@docusaurus/useBaseUrl"; -import { - Button, - Card, - Subtitle1, - Text, - Title1, - Title2, - makeStyles, - tokens, -} from "@fluentui/react-components"; -import { FluentImageName, FluentImg } from "../fluent-img"; +import { Button, Text, Title1, Title2, makeStyles, tokens } from "@fluentui/react-components"; +import { Card } from "../card/card"; +import { SectionedLayout } from "../sectioned-layout/sectioned-layout"; +import { Feature, FeatureGroup } from "./feature/feature"; import style from "./homepage.module.css"; import { Section } from "./section/section"; @@ -19,19 +12,17 @@ const useFluentStyles = makeStyles({ }); export const HomeContent = () => { - const fluentStyles = useFluentStyles(); - return ( -
+ <> -
+ - -
-
+ + + ); }; @@ -77,7 +68,7 @@ const Overview = () => { generate standards-compliant API schemas in seconds. -
+ Reduce the time it takes to describe complex API shapes by using a minimal language that's easy for developers to use and love. @@ -90,29 +81,12 @@ const Overview = () => { With a single line of code, generate a multitude of API assets in your preferred format or protocol - even all at the same time. -
+ ); }; -interface FeatureProps { - title: string; - image: FluentImageName; - children: React.ReactNode; -} -const Feature = ({ title, image, children }: FeatureProps) => { - return ( - -
- - {title} - {children} -
-
- ); -}; - const OpenAPISection = () => { return (
{ +export const Section = ({ header, title, description, items, layout }: SectionProps) => { const fluentStyles = useFluentStyles(); return ( -
-
-
-
- - {header} - - {title} -
-
- {description} +
+
+
+
+
+ + {header} + + {title} +
+
+ {description} +
+ + {items.map((x, i) => ( + <> + {i !== 0 ? : ""} + + + ))} +
- - {items.map((x, i) => ( - <> - {i !== 0 ? : ""} - - - ))} -
@@ -70,12 +79,12 @@ const SectionItem = ({ title, description, image, link }: SectionItem) => { image={} header={{title}} description={ - <> +
{description} Learn more - +
} > ); diff --git a/packages/website/src/components/sectioned-layout/sectioned-layout.module.css b/packages/website/src/components/sectioned-layout/sectioned-layout.module.css new file mode 100644 index 0000000000..035739cf90 --- /dev/null +++ b/packages/website/src/components/sectioned-layout/sectioned-layout.module.css @@ -0,0 +1,10 @@ +.layout { + display: flex; + padding: 48px 0px 60px 0px; + margin: auto; + max-width: 1464px; + flex-direction: column; + align-items: center; + gap: 128px; + align-self: stretch; +} diff --git a/packages/website/src/components/sectioned-layout/sectioned-layout.tsx b/packages/website/src/components/sectioned-layout/sectioned-layout.tsx new file mode 100644 index 0000000000..4a2509ef71 --- /dev/null +++ b/packages/website/src/components/sectioned-layout/sectioned-layout.tsx @@ -0,0 +1,5 @@ +import style from "./sectioned-layout.module.css"; + +export const SectionedLayout = ({ children }) => { + return
{children}
; +}; diff --git a/packages/website/src/components/use-case-feature/use-case-feature.module.css b/packages/website/src/components/use-case-feature/use-case-feature.module.css new file mode 100644 index 0000000000..6d17e17606 --- /dev/null +++ b/packages/website/src/components/use-case-feature/use-case-feature.module.css @@ -0,0 +1,49 @@ +.feature-group { + display: flex; + align-items: stretch; + align-self: center; + flex-direction: column; + padding: 0 80px; +} + +.feature { + display: flex; + flex-direction: column; + align-items: center; + gap: 24px; + max-width: 320px; +} + +.content { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; +} + +.image { + width: 64px; + height: 64px; +} + +@media only screen and (min-width: 1200px) { + .feature-card { + width: 350px; + } +} + +@media only screen and (min-width: 1024px) { + .feature-group { + width: 100%; + flex-direction: row; + justify-content: space-between; + } + + .feature { + align-items: baseline; + } + + .content { + align-items: baseline; + } +} diff --git a/packages/website/src/components/use-case-feature/use-case-feature.tsx b/packages/website/src/components/use-case-feature/use-case-feature.tsx new file mode 100644 index 0000000000..3f3f008471 --- /dev/null +++ b/packages/website/src/components/use-case-feature/use-case-feature.tsx @@ -0,0 +1,26 @@ +import { Link, Subtitle1, Text } from "@fluentui/react-components"; +import { FluentImageName, FluentImg } from "../fluent-img"; +import style from "./use-case-feature.module.css"; + +export interface UseCaseFeatureProps { + image: FluentImageName; + title: string; + subtitle: string; + link: string; +} +export const UseCaseFeature = ({ image, title, subtitle, link }: UseCaseFeatureProps) => { + return ( +
+ +
+ {title} + {subtitle} + Learn more +
+
+ ); +}; + +export const UseCaseFeatureGroup = ({ children }) => { + return
{children}
; +}; diff --git a/packages/website/src/components/use-case-overview/use-case-overview.module.css b/packages/website/src/components/use-case-overview/use-case-overview.module.css new file mode 100644 index 0000000000..8dc9315b51 --- /dev/null +++ b/packages/website/src/components/use-case-overview/use-case-overview.module.css @@ -0,0 +1,64 @@ +.container { + padding-top: 100px; + padding-bottom: 80px; +} + +.overview { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + align-content: center; + + margin: auto; +} + +.content { + flex: 1; + padding: 80px; +} + +.spacer { + height: 32px; +} + +.title { + max-width: 522px; +} +.subtitle { + max-width: 522px; +} + +.illustration { + width: 30vw; + height: 25vw; +} + +@media only screen and (min-width: 1200px) { + .overview { + max-width: 1464px; + height: 600px; + } + + .illustration { + width: 30vw; + height: 25vw; + } +} + +@media only screen and (min-width: 1024px) { + .overview { + flex-direction: row; + } + + .illustration { + width: 40vw; + height: 32vw; + } +} + +@media only screen and (max-width: 1023px) { + .illustration { + display: none; + } +} diff --git a/packages/website/src/components/use-case-overview/use-case-overview.tsx b/packages/website/src/components/use-case-overview/use-case-overview.tsx new file mode 100644 index 0000000000..0051bc8a7f --- /dev/null +++ b/packages/website/src/components/use-case-overview/use-case-overview.tsx @@ -0,0 +1,32 @@ +import { Button, Title2 } from "@fluentui/react-components"; +import { Card } from "../card/card"; +import { DescriptionText } from "../description-text/secondary-text"; +import style from "./use-case-overview.module.css"; + +export interface UseCaseOverviewProps { + title: string; + subtitle: string; + link: string; +} + +export const UseCaseOverview = (props: UseCaseOverviewProps) => { + return ( +
+
+
+ + {props.title} + +
+ {props.subtitle} +
+ +
+ + +
+
+ ); +}; diff --git a/packages/website/src/pages/data-validation.tsx b/packages/website/src/pages/data-validation.tsx index 17ed11834e..7262105c80 100644 --- a/packages/website/src/pages/data-validation.tsx +++ b/packages/website/src/pages/data-validation.tsx @@ -1,5 +1,79 @@ import { FluentLayout } from "../components/fluent-layout"; +import { Section } from "../components/homepage/section/section"; +import { SectionedLayout } from "../components/sectioned-layout/sectioned-layout"; +import { + UseCaseFeature, + UseCaseFeatureGroup, +} from "../components/use-case-feature/use-case-feature"; +import { UseCaseOverview } from "../components/use-case-overview/use-case-overview"; export default function Home() { - return ; + return ( + + + + ); } + +const DataValidationContent = () => { + return ( +
+ + + + + + + + +
+ +
+ +
+ ); +}; diff --git a/packages/website/src/pages/json-rpc.tsx b/packages/website/src/pages/json-rpc.tsx index 17ed11834e..98bce82161 100644 --- a/packages/website/src/pages/json-rpc.tsx +++ b/packages/website/src/pages/json-rpc.tsx @@ -1,5 +1,79 @@ import { FluentLayout } from "../components/fluent-layout"; +import { Section } from "../components/homepage/section/section"; +import { SectionedLayout } from "../components/sectioned-layout/sectioned-layout"; +import { + UseCaseFeature, + UseCaseFeatureGroup, +} from "../components/use-case-feature/use-case-feature"; +import { UseCaseOverview } from "../components/use-case-overview/use-case-overview"; export default function Home() { - return ; + return ( + + + + ); } + +const JsonRpcContent = () => { + return ( +
+ + + + + + + + +
+ +
+ +
+ ); +}; diff --git a/packages/website/src/pages/openapi.tsx b/packages/website/src/pages/openapi.tsx index 17ed11834e..78aa48d226 100644 --- a/packages/website/src/pages/openapi.tsx +++ b/packages/website/src/pages/openapi.tsx @@ -1,5 +1,79 @@ import { FluentLayout } from "../components/fluent-layout"; +import { Section } from "../components/homepage/section/section"; +import { SectionedLayout } from "../components/sectioned-layout/sectioned-layout"; +import { + UseCaseFeature, + UseCaseFeatureGroup, +} from "../components/use-case-feature/use-case-feature"; +import { UseCaseOverview } from "../components/use-case-overview/use-case-overview"; export default function Home() { - return ; + return ( + + + + ); } + +const OpenApiContent = () => { + return ( +
+ + + + + + + + +
+ +
+ +
+ ); +};