mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-10 07:38:04 -05:00
feat(frontend): document typography tokens + Text component (#10132)
### Changes 🏗️ <img width="700" alt="Screenshot 2025-06-09 at 17 01 59" src="https://github.com/user-attachments/assets/f2b0a3a6-fdf1-4e3e-9caa-d2bf03543dab" /> <img width="700" alt="Screenshot 2025-06-09 at 17 02 06" src="https://github.com/user-attachments/assets/36e27a0b-07f2-4074-8628-cb236d75e4c4" /> This PR introduces a comprehensive Typography System for our design system with improved documentation and developer experience [matching what we have on Figma](https://www.figma.com/design/nO9NFynNuicLtkiwvOxrbz/AutoGPT-Design-System?m=dev). #### **Typography System** - Created `<Text />` component - Enforce its usage to ensure consistent typographic styles across the app ```tsx <Text variant="h1">Heading 1</Text> <Text variant="h2">Heading 2</Text> <Text variant="body">hello world</Text> <Text variant="small">smol text</Text> ``` - Created `Typography.stories.tsx` on Storybook - Complete typography overview with font showcases and usage guidelines #### **Storybook Improvements** - **Updated TypeScript docgen** configuration for better prop extraction - **Cleaned up story rendering** to prevent MDX styling pollution - **Split large story files** into focused, maintainable components ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: **Test Plan:** - [x] Typography stories render correctly in Storybook - [x] All Text component variants display properly - [x] Interactive playground controls function correctly - [x] No TypeScript or linting errors --------- Co-authored-by: Swifty <craigswift13@gmail.com>
This commit is contained in:
@@ -8,13 +8,14 @@ const config: StorybookConfig = {
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
"@storybook/addon-interactions",
|
||||
"@storybook/addon-docs",
|
||||
],
|
||||
features: {
|
||||
experimentalRSC: true,
|
||||
},
|
||||
framework: {
|
||||
name: "@storybook/nextjs",
|
||||
options: {},
|
||||
options: { builder: { useSWC: true } },
|
||||
},
|
||||
staticDirs: ["../public"],
|
||||
};
|
||||
|
||||
@@ -3,6 +3,15 @@ import type { Preview } from "@storybook/react";
|
||||
import { initialize, mswLoader } from "msw-storybook-addon";
|
||||
import "../src/app/globals.css";
|
||||
import "../src/components/styles/fonts.css";
|
||||
import {
|
||||
Controls,
|
||||
Description,
|
||||
Primary,
|
||||
Source,
|
||||
Stories,
|
||||
Subtitle,
|
||||
Title,
|
||||
} from "@storybook/blocks";
|
||||
|
||||
// Initialize MSW
|
||||
initialize();
|
||||
@@ -12,19 +21,26 @@ const preview: Preview = {
|
||||
nextjs: {
|
||||
appDirectory: true,
|
||||
},
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
docs: {
|
||||
page: () => (
|
||||
<>
|
||||
<Title />
|
||||
<Subtitle />
|
||||
<Description />
|
||||
<Primary />
|
||||
<Source />
|
||||
<Stories />
|
||||
<Controls />
|
||||
</>
|
||||
),
|
||||
},
|
||||
},
|
||||
loaders: [mswLoader],
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<>
|
||||
<div className="bg-background p-8">
|
||||
<Story />
|
||||
</>
|
||||
</div>
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
"@chromatic-com/storybook": "3.2.6",
|
||||
"@playwright/test": "1.52.0",
|
||||
"@storybook/addon-a11y": "8.6.14",
|
||||
"@storybook/addon-docs": "8.6.14",
|
||||
"@storybook/addon-essentials": "8.6.14",
|
||||
"@storybook/addon-interactions": "8.6.14",
|
||||
"@storybook/addon-links": "8.6.14",
|
||||
|
||||
3
autogpt_platform/frontend/pnpm-lock.yaml
generated
3
autogpt_platform/frontend/pnpm-lock.yaml
generated
@@ -207,6 +207,9 @@ importers:
|
||||
'@storybook/addon-a11y':
|
||||
specifier: 8.6.14
|
||||
version: 8.6.14(storybook@8.6.14(prettier@3.5.3))
|
||||
'@storybook/addon-docs':
|
||||
specifier: 8.6.14
|
||||
version: 8.6.14(@types/react@18.3.17)(storybook@8.6.14(prettier@3.5.3))
|
||||
'@storybook/addon-essentials':
|
||||
specifier: 8.6.14
|
||||
version: 8.6.14(@types/react@18.3.17)(storybook@8.6.14(prettier@3.5.3))
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { Text, textVariants, type TextVariant } from "./Text";
|
||||
import { StoryCode } from "@/stories/helpers/StoryCode";
|
||||
|
||||
const meta: Meta<typeof Text> = {
|
||||
title: "Design System/Atoms/Text",
|
||||
component: Text,
|
||||
tags: ["autodocs"],
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
controls: { hideNoControlsWarning: true },
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"A flexible Text component that supports all typography variants from our design system. Uses Poppins for headings and Geist Sans for body text.",
|
||||
},
|
||||
source: {
|
||||
state: "open",
|
||||
},
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: { type: "select" },
|
||||
options: textVariants,
|
||||
description: "Typography variant to apply",
|
||||
},
|
||||
as: {
|
||||
control: { type: "select" },
|
||||
options: ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span", "div", "code"],
|
||||
description: "HTML element to render as",
|
||||
},
|
||||
children: {
|
||||
control: "text",
|
||||
description: "Text content",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Text>;
|
||||
|
||||
//=============================================================================
|
||||
// All Variants Overview
|
||||
//=============================================================================
|
||||
export function AllVariants() {
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* Headings */}
|
||||
<div className="mb-19 mb-20 space-y-6">
|
||||
<h2 className="mb-4 border-b border-border pb-2 text-xl text-zinc-500">
|
||||
Headings
|
||||
</h2>
|
||||
<Text variant="h1">Heading 1</Text>
|
||||
<Text variant="h2">Heading 2</Text>
|
||||
<Text variant="h3">Heading 3</Text>
|
||||
<Text variant="h4">Heading 4</Text>
|
||||
<StoryCode
|
||||
code={`<Text variant="h1">Heading 1</Text>
|
||||
<Text variant="h2">Heading 2</Text>
|
||||
<Text variant="h3">Heading 3</Text>
|
||||
<Text variant="h4">Heading 4</Text>`}
|
||||
/>
|
||||
</div>
|
||||
{/* Body Text */}
|
||||
<h2 className="mb-4 border-b border-border pb-2 text-xl text-zinc-500">
|
||||
Body Text
|
||||
</h2>
|
||||
<Text variant="lead">Lead</Text>
|
||||
<StoryCode code={`<Text variant="lead">Lead</Text>`} />
|
||||
<div className="flex flex-row gap-8">
|
||||
<Text variant="large">Large</Text>
|
||||
<Text variant="large-medium">Large Medium</Text>
|
||||
<Text variant="large-semibold">Large Semibold</Text>
|
||||
</div>
|
||||
<StoryCode
|
||||
code={`<Text variant="large">Large</Text>
|
||||
<Text variant="large-medium">Large Medium</Text>
|
||||
<Text variant="large-semibold">Large Semibold</Text>`}
|
||||
/>
|
||||
<div className="flex flex-row gap-8">
|
||||
<Text variant="body">Body</Text>
|
||||
<Text variant="body-medium">Body Medium</Text>
|
||||
</div>
|
||||
<StoryCode
|
||||
code={`<Text variant="body">Body</Text>
|
||||
<Text variant="body-medium">Body Medium</Text>`}
|
||||
/>
|
||||
<div className="flex flex-row gap-8">
|
||||
<Text variant="small">Small</Text>
|
||||
<Text variant="small-medium">Small Medium</Text>
|
||||
</div>
|
||||
<StoryCode
|
||||
code={`<Text variant="small">Small</Text>
|
||||
<Text variant="small-medium">Small Medium</Text>`}
|
||||
/>
|
||||
<Text variant="subtle">Subtle</Text>
|
||||
<StoryCode code={`<Text variant="subtle">Subtle</Text>`} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Headings Only
|
||||
//=============================================================================
|
||||
export function Headings() {
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<Text variant="h1">Heading 1</Text>
|
||||
<Text variant="h2">Heading 2</Text>
|
||||
<Text variant="h3">Heading 3</Text>
|
||||
<Text variant="h4">Heading 4</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Body Text Only
|
||||
//=============================================================================
|
||||
export function BodyText() {
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<Text variant="lead">Lead</Text>
|
||||
<Text variant="large">Large</Text>
|
||||
<Text variant="large-medium">Large Medium</Text>
|
||||
<Text variant="large-semibold">Large Semibold</Text>
|
||||
<Text variant="body">Body</Text>
|
||||
<Text variant="body-medium">Body Medium</Text>
|
||||
<Text variant="small">Small</Text>
|
||||
<Text variant="small-medium">Small Medium</Text>
|
||||
<Text variant="subtle">Subtle</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Interactive Playground
|
||||
//=============================================================================
|
||||
export const Playground: Story = {
|
||||
args: {
|
||||
variant: "body",
|
||||
children:
|
||||
"Edit this text and try different variants via the controls below",
|
||||
},
|
||||
parameters: {
|
||||
controls: { include: ["variant", "as", "children"] },
|
||||
},
|
||||
render: (args) => (
|
||||
<div className="space-y-8">
|
||||
<Text {...args} />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// Custom Element
|
||||
//=============================================================================
|
||||
export function CustomElement() {
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<Text variant="h3" as="div">
|
||||
H3 size rendered as div
|
||||
</Text>
|
||||
<Text variant="body" as="h2">
|
||||
Body size rendered as h2
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
37
autogpt_platform/frontend/src/components/_new/Text/Text.tsx
Normal file
37
autogpt_platform/frontend/src/components/_new/Text/Text.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import { As, Variant, variantElementMap, variants } from "./helpers";
|
||||
|
||||
type CustomProps = {
|
||||
variant: Variant;
|
||||
as?: As;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export type TextProps = React.PropsWithChildren<
|
||||
CustomProps & React.ComponentPropsWithoutRef<"p">
|
||||
>;
|
||||
|
||||
export function Text({
|
||||
children,
|
||||
variant,
|
||||
as: outerAs,
|
||||
className = "",
|
||||
...rest
|
||||
}: TextProps) {
|
||||
const variantClasses = variants[variant] || variants.body;
|
||||
const Element = outerAs || variantElementMap[variant];
|
||||
const combinedClassName = `${variantClasses} ${className}`.trim();
|
||||
|
||||
return React.createElement(
|
||||
Element,
|
||||
{
|
||||
className: combinedClassName,
|
||||
...rest,
|
||||
},
|
||||
children,
|
||||
);
|
||||
}
|
||||
|
||||
// Export variant names for use in stories
|
||||
export const textVariants = Object.keys(variants) as Variant[];
|
||||
export type TextVariant = Variant;
|
||||
@@ -0,0 +1,53 @@
|
||||
export type As =
|
||||
| "h1"
|
||||
| "h2"
|
||||
| "h3"
|
||||
| "h4"
|
||||
| "h5"
|
||||
| "h6"
|
||||
| "p"
|
||||
| "span"
|
||||
| "div"
|
||||
| "code"
|
||||
| "label"
|
||||
| "kbd";
|
||||
|
||||
export const variants = {
|
||||
// Headings
|
||||
h1: "font-poppins text-5xl font-semibold leading-[56px] text-zinc-800",
|
||||
h2: "font-poppins text-4xl font-normal leading-[52px] text-zinc-800",
|
||||
h3: "font-poppins text-3xl font-medium leading-10 text-zinc-800",
|
||||
h4: "font-poppins text-base font-medium leading-normal text-zinc-800",
|
||||
|
||||
// Body Text
|
||||
lead: "font-sans text-xl font-normal leading-loose text-muted-zinc-800",
|
||||
large: "font-sans text-base font-normal leading-normal text-zinc-800",
|
||||
"large-medium":
|
||||
"font-sans text-base font-medium leading-normal text-zinc-800",
|
||||
"large-semibold":
|
||||
"font-sans text-base font-semibold leading-normal text-zinc-800",
|
||||
body: "font-sans text-sm font-normal leading-snug text-zinc-800",
|
||||
"body-medium": "font-sans text-sm font-medium leading-snug text-zinc-800",
|
||||
small: "font-sans text-xs font-normal leading-tight text-zinc-800",
|
||||
"small-medium": "font-sans text-xs font-medium leading-tight text-zinc-800",
|
||||
subtle:
|
||||
"font-sans text-xs font-medium uppercase leading-tight tracking-wide text-zinc-800",
|
||||
} as const;
|
||||
|
||||
export type Variant = keyof typeof variants;
|
||||
|
||||
export const variantElementMap: Record<Variant, As> = {
|
||||
h1: "h1",
|
||||
h2: "h2",
|
||||
h3: "h3",
|
||||
h4: "h4",
|
||||
lead: "p",
|
||||
large: "p",
|
||||
"large-medium": "p",
|
||||
"large-semibold": "p",
|
||||
body: "p",
|
||||
"body-medium": "p",
|
||||
small: "p",
|
||||
"small-medium": "p",
|
||||
subtle: "p",
|
||||
};
|
||||
176
autogpt_platform/frontend/src/stories/Typography.stories.tsx
Normal file
176
autogpt_platform/frontend/src/stories/Typography.stories.tsx
Normal file
@@ -0,0 +1,176 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { Text } from "@/components/_new/Text/Text";
|
||||
import { StoryCode } from "@/stories/helpers/StoryCode";
|
||||
|
||||
const meta: Meta<typeof Text> = {
|
||||
title: "Design System/ Tokens /Typography",
|
||||
component: Text,
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export function AllVariants() {
|
||||
return (
|
||||
<div className="space-y-12">
|
||||
{/* Typography System Documentation */}
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h1 className="mb-4 text-4xl font-bold text-zinc-800">
|
||||
Typography System
|
||||
</h1>
|
||||
<p className="text-lg leading-relaxed text-zinc-600">
|
||||
Our typography system uses two carefully selected fonts to create a
|
||||
clear hierarchy and excellent readability across all interfaces.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-8 md:grid-cols-2">
|
||||
<div>
|
||||
<h2 className="mb-4 text-2xl font-semibold text-zinc-800">
|
||||
Font Families
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-lg border border-gray-200 p-4">
|
||||
<h3 className="mb-2 font-semibold text-zinc-800">
|
||||
<a
|
||||
href="https://fonts.google.com/specimen/Poppins"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 hover:underline"
|
||||
>
|
||||
Poppins
|
||||
</a>
|
||||
</h3>
|
||||
<p className="mb-2 text-sm text-zinc-600">
|
||||
Used for all headings and display text
|
||||
</p>
|
||||
<div className="font-poppins text-2xl text-zinc-800">
|
||||
The quick brown fox
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-lg border border-gray-200 p-4">
|
||||
<h3 className="mb-2 font-semibold text-zinc-800">
|
||||
<a
|
||||
href="https://github.com/vercel/geist-font"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 hover:underline"
|
||||
>
|
||||
Geist Sans
|
||||
</a>
|
||||
</h3>
|
||||
<p className="mb-2 text-sm text-zinc-600">
|
||||
Used for all body text, labels, and UI elements
|
||||
</p>
|
||||
<div className="font-sans text-base text-zinc-800">
|
||||
The quick brown fox jumps over the lazy dog
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="mb-4 text-2xl font-semibold text-zinc-800">FAQ</h2>
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-lg border border-gray-200 p-4">
|
||||
<h3 className="mb-2 font-semibold text-zinc-800">
|
||||
🤔 Why can't I use <p> tags directly?
|
||||
</h3>
|
||||
<div className="space-y-3 text-zinc-600">
|
||||
<p className="text-sm">
|
||||
Always use the{" "}
|
||||
<code className="rounded bg-gray-100 px-2 py-1 text-xs">
|
||||
<Text />
|
||||
</code>{" "}
|
||||
component instead of plain HTML elements like{" "}
|
||||
<code className="rounded bg-gray-100 px-2 py-1 text-xs">
|
||||
<h1>
|
||||
</code>
|
||||
,{" "}
|
||||
<code className="rounded bg-gray-100 px-2 py-1 text-xs">
|
||||
<p>
|
||||
</code>
|
||||
,{" "}
|
||||
<code className="rounded bg-gray-100 px-2 py-1 text-xs">
|
||||
<span>
|
||||
</code>
|
||||
, etc... Reasons:
|
||||
</p>
|
||||
<ul className="ml-4 list-inside list-disc space-y-1 text-sm">
|
||||
<li>Ensures consistent typography across the entire app</li>
|
||||
<li>
|
||||
Makes future design updates easier (change once, update
|
||||
everywhere)
|
||||
</li>
|
||||
<li>Provides TypeScript safety for typography variants</li>
|
||||
<li>
|
||||
Automatically maps to correct HTML elements for
|
||||
accessibility
|
||||
</li>
|
||||
<li>Prevents styling inconsistencies and design drift</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Typography Examples */}
|
||||
<div className="space-y-8">
|
||||
<div className="mb-19 mb-20 space-y-6">
|
||||
<h2 className="mb-4 border-b border-border pb-2 text-xl text-zinc-500">
|
||||
Headings (Poppins)
|
||||
</h2>
|
||||
<Text variant="h1">Heading 1</Text>
|
||||
<Text variant="h2">Heading 2</Text>
|
||||
<Text variant="h3">Heading 3</Text>
|
||||
<Text variant="h4">Heading 4</Text>
|
||||
<StoryCode
|
||||
code={`<Text variant="h1">Heading 1</Text>
|
||||
<Text variant="h2">Heading 2</Text>
|
||||
<Text variant="h3">Heading 3</Text>
|
||||
<Text variant="h4">Heading 4</Text>`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h2 className="mb-4 border-b border-border pb-2 text-xl text-zinc-500">
|
||||
Body Text (Geist Sans)
|
||||
</h2>
|
||||
<Text variant="lead">Lead</Text>
|
||||
<StoryCode code="<Text variant='lead'>Lead</Text>" />
|
||||
<div className="flex flex-row gap-8">
|
||||
<Text variant="large">Large</Text>
|
||||
<Text variant="large-medium">Large Medium</Text>
|
||||
<Text variant="large-semibold">Large Semibold</Text>
|
||||
</div>
|
||||
<StoryCode
|
||||
code={`<Text variant="large">Large</Text>
|
||||
<Text variant="large-medium">Large Medium</Text>
|
||||
<Text variant="large-semibold">Large Semibold</Text>`}
|
||||
/>
|
||||
<div className="flex flex-row gap-8">
|
||||
<Text variant="body">Body</Text>
|
||||
<Text variant="body-medium">Body Medium</Text>
|
||||
</div>
|
||||
<StoryCode
|
||||
code={`<Text variant="body">Body</Text>
|
||||
<Text variant="body-medium">Body Medium</Text>`}
|
||||
/>
|
||||
<div className="flex flex-row gap-8">
|
||||
<Text variant="small">Small</Text>
|
||||
<Text variant="small-medium">Small Medium</Text>
|
||||
</div>
|
||||
<StoryCode
|
||||
code={`<Text variant="small">Small</Text>
|
||||
<Text variant="small-medium">Small Medium</Text>`}
|
||||
/>
|
||||
<Text variant="subtle">Subtle</Text>
|
||||
<StoryCode code={`<Text variant="subtle">Subtle</Text>`} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
11
autogpt_platform/frontend/src/stories/helpers/StoryCode.tsx
Normal file
11
autogpt_platform/frontend/src/stories/helpers/StoryCode.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
type Props = {
|
||||
code: string;
|
||||
};
|
||||
|
||||
export function StoryCode(props: Props) {
|
||||
return (
|
||||
<pre className="block rounded border bg-zinc-100 px-3 py-2 font-mono text-xs text-indigo-800 shadow-sm">
|
||||
{props.code}
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user