diff --git a/.changeset/type-role-classes.md b/.changeset/type-role-classes.md new file mode 100644 index 000000000..8596626c1 --- /dev/null +++ b/.changeset/type-role-classes.md @@ -0,0 +1,6 @@ +--- +'@workflowbuilder/ui': minor +'@workflowbuilder/sdk': minor +--- + +Add the Design System 2.0 type roles as `wb-text-{family}-{size}[-emphasized]` utility classes (39 styles: Display, Headline, Title, Body, Label, Node, UI/Code) in `@workflowbuilder/ui`, Poppins and Inter (for the `wb-text-code` role) now ship with `@workflowbuilder/ui` itself, so the classes work standalone; the SDK inherits the fonts through the UI package instead of bundling its own. Existing `ax-public-*` typography classes are unchanged. diff --git a/apps/docs/astro.config.mjs b/apps/docs/astro.config.mjs index 0453e265c..1026dbbc2 100644 --- a/apps/docs/astro.config.mjs +++ b/apps/docs/astro.config.mjs @@ -178,6 +178,7 @@ export default defineConfig({ items: [ { label: 'Overview', link: '/ui-library/overview/' }, { label: 'Design tokens', link: '/ui-library/design-tokens/' }, + { label: 'Typography', link: '/ui-library/typography/' }, { label: 'UI Components', autogenerate: { directory: 'ui-library/ui-components' } }, { label: 'Diagram Components', autogenerate: { directory: 'ui-library/diagram-components' } }, ], diff --git a/apps/docs/src/content/docs/ui-library/typography.mdx b/apps/docs/src/content/docs/ui-library/typography.mdx new file mode 100644 index 000000000..298fa4b7e --- /dev/null +++ b/apps/docs/src/content/docs/ui-library/typography.mdx @@ -0,0 +1,59 @@ +--- +title: Typography +description: The type roles shipped by @workflowbuilder/ui as wb-text-* utility classes. +--- + +`@workflowbuilder/ui` ships the type scale as utility +classes: one class per text style, named `wb-text-{family}-{size}` with an +optional `-emphasized` suffix. A class carries the complete style — family, +size, weight, and line-height — so text never hard-codes font values. + +```html +

Workflow settings

+

Configure how each node runs.

+Connection: active +``` + +Two typefaces ship with `@workflowbuilder/ui` itself (via the root import; subpath-only consumers load them independently): **Poppins** for every UI role and +**Inter** for the `wb-text-code` role. Emphasized maps to SemiBold (600), +Regular to 400. + +## The scale + +| Family | Used for | Classes (`wb-text-…`) | px | +| -------- | ------------------------------ | ---------------------------------------------- | ----------------- | +| Display | Hero moments, big empty states | `display-s` · `display-m` · `display-l` | 24 · 28 · 32 | +| Headline | Page titles, major sections | `headline-s` · `headline-m` · `headline-l` | 20 · 22 · 24 | +| Title | Card titles, group labels | `title-s` · `title-m` · `title-l` | 14 · 16 · 18 | +| Body | Running text, descriptions | `body-s` · `body-m` · `body-l` | 12 · 14 · 16 | +| Label | Buttons, chips, captions | `label-s` · `label-m` · `label-l` · `label-xl` | 10 · 12 · 14 · 16 | +| Node | Canvas node and port labels | `node-s` · `node-m` · `node-l` | 11 · 13 · 16 | +| UI/Code | Code, token names, IDs | `code` (Regular only) | 12 | + +Every family except UI/Code doubles with an `-emphasized` variant +(`wb-text-title-m-emphasized`), for 39 styles total. + +Which role to reach for: + +| Context | Recommended role | +| ------------------------------- | -------------------------------- | +| Page / hero title | `display-*` or `headline-l` | +| Major section heading | `headline-s`/`-m` or `title-l` | +| Card or group title | `title-s`/`-m` | +| Running text and descriptions | `body-m` (`body-s` for dense UI) | +| Captions, helper text, metadata | `label-m`/`-l` | +| Buttons and chips | `label-l-emphasized` | +| Canvas node and port labels | `node-s`/`-m` | +| Code, token names, IDs | `code` | + +## Font-size tokens + +Class font sizes reference the `--wb-font-size-*` primitives +(`wb/font-size/NNN` in Figma, where `NNN` ≈ px × 12.5, truncated; the CSS values are +rem). Until the token export ships them through +`@workflowbuilder/ui/tokens.css`, the classes define these primitives +themselves — the migration swaps the source without touching consumers. + +The pre-2.0 `ax-public-*` typography classes are deprecated: components move +to `wb-text-*` as they are redesigned, and the old classes are removed in +3.0.0. Do not adopt them in new code. diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 7ed4d2bb9..e11e10f14 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -214,7 +214,7 @@ Available `--wb-*` tokens: `--wb-background-color`, `--wb-font-family`, `--wb-tr Deeper color and spacing customization (palette, semantic UI tokens) goes through the `--ax-*` token layer from `@workflowbuilder/ui`. Full guide: [Design system and customization](https://www.workflowbuilder.io/docs/overview/features/design-system-and-customization/). -**Prefix ownership.** The `--wb-*` namespace is shared by two owners, split by convention: names with a design-domain segment (`--wb-ui-*`, `--wb-components-*`, `--wb-canvas-*`, `--wb-colors-*`, `--wb-space-*`, `--wb-radius-*`, `--wb-size-*`, `--wb-font-size-*`, `--wb-shadow-*`) belong to the design-token export and will arrive with Design System 2.0; everything else under `--wb-*` (like the theming variables above) is defined by the SDK itself. The two sets do not collide because exported names always carry a domain segment. Every `var(--…)` in workspace CSS is validated in CI against the set of names that actually exist (stylelint, see `packages/tokens/README.md`). +**Prefix ownership.** The `--wb-*` namespace is shared by two owners, split by convention: names with a design-domain segment (`--wb-ui-*`, `--wb-components-*`, `--wb-canvas-*`, `--wb-colors-*`, `--wb-space-*`, `--wb-radius-*`, `--wb-size-*`, `--wb-font-size-*`, `--wb-shadow-*`) belong to the design-token export; everything else under `--wb-*` (like the theming variables above) is defined by the SDK itself. The two sets do not collide because exported names always carry a domain segment. Every `var(--…)` in workspace CSS is validated in CI against the set of names that actually exist (stylelint, see `packages/tokens/README.md`). ## CSS — global resets diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 1234c2c6e..e8d8abe32 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -72,7 +72,6 @@ "dependencies": { "@base-ui/react": "catalog:", "@cfworker/json-schema": "4.1.1", - "@fontsource/poppins": "^5.2.7", "@jsonforms/core": "^3.4.0", "@jsonforms/react": "^3.4.0", "@phosphor-icons/react": "^2.1.7", diff --git a/packages/sdk/src/index.css b/packages/sdk/src/index.css index c5fec53ae..a9cf22c47 100644 --- a/packages/sdk/src/index.css +++ b/packages/sdk/src/index.css @@ -6,20 +6,8 @@ See packages/ui/css-layers.md. */ @layer ui.base, ui.component; -/* Poppins is bundled rather than fetched from a CDN: strict CSPs block the - request, the pre-consent IP leak breaks GDPR flows, and air-gapped - deployments have no outbound internet. */ -@import '@fontsource/poppins/latin-300.css'; -@import '@fontsource/poppins/latin-400.css'; -@import '@fontsource/poppins/latin-500.css'; -@import '@fontsource/poppins/latin-600.css'; -@import '@fontsource/poppins/latin-700.css'; -@import '@fontsource/poppins/latin-ext-300.css'; -@import '@fontsource/poppins/latin-ext-400.css'; -@import '@fontsource/poppins/latin-ext-500.css'; -@import '@fontsource/poppins/latin-ext-600.css'; -@import '@fontsource/poppins/latin-ext-700.css'; - +/* Poppins and Inter ship with @workflowbuilder/ui (next to the wb-text-* + classes that require them) and arrive through the JS module graph. */ @import '@workflowbuilder/ui/tokens.css'; /* @workflowbuilder/ui styles arrive through the JS module graph - importing @@ -35,6 +23,10 @@ --wb-scroll-radius: 0.625rem; --wb-scroll-thumb-color: var(--ax-scrollbar-bg-default); --wb-scroll-track-color: transparent; + /* Candidate for removal at the 3.0.0 close-out: the wb-text-* classes + carry their own families, so this variable loses reach as components + adopt them — decide then between removing it and narrowing it to + consumer-slot content. */ --wb-font-family: 'Poppins', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; } } diff --git a/packages/ui/combine-css-bundle.mts b/packages/ui/combine-css-bundle.mts index 351944def..83777da77 100644 --- a/packages/ui/combine-css-bundle.mts +++ b/packages/ui/combine-css-bundle.mts @@ -75,7 +75,7 @@ function writeCombinedStylesheet(distributionDirectory: string, stylesDirectory: } function writeGlobalStylesheet(distributionDirectory: string, stylesDirectory: string) { - const globals = ['layers.css', 'globals.css', 'typography.css'] + const globals = ['layers.css', 'globals.css', '_provisional.css', 'typography.css'] .map((file) => fs.readFileSync(path.resolve(stylesDirectory, file), 'utf8')) .join('\n'); diff --git a/packages/ui/package.json b/packages/ui/package.json index 2edd03db7..5e9a3ea7a 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -65,6 +65,8 @@ "react-textarea-autosize": "^8.5.6" }, "devDependencies": { + "@fontsource/inter": "^5.3.0", + "@fontsource/poppins": "^5.3.0", "@types/react": "catalog:", "@types/react-dom": "^19.1.0", "@workflowbuilder/ui-tokens": "workspace:*", diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 66c82ded0..d50590eba 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -1,3 +1,5 @@ +import './styles/_provisional.css'; +import './styles/fonts.css'; import './styles/globals.css'; import './styles/layers.css'; import './styles/typography.css'; diff --git a/packages/ui/src/styles/_provisional.css b/packages/ui/src/styles/_provisional.css new file mode 100644 index 000000000..2a5a192cb --- /dev/null +++ b/packages/ui/src/styles/_provisional.css @@ -0,0 +1,23 @@ +/* PROVISIONAL — this whole file is scheduled for deletion. + Tokens the Figma token export does not ship yet, defined here so the + classes that consume them work today. When the export lands in tokens.css + its unlayered definitions override this layered block automatically; then + delete this file together with its two wire-ups (src/index.ts import and + the combine-css-bundle globals list) — both fail the build if forgotten. */ +@layer ui.base { + :root { + /* wb/font-size/NNN, NNN ≈ px × 12.5 (truncated). */ + --wb-font-size-125: 0.625rem; + --wb-font-size-137: 0.6875rem; + --wb-font-size-150: 0.75rem; + --wb-font-size-162: 0.8125rem; + --wb-font-size-175: 0.875rem; + --wb-font-size-200: 1rem; + --wb-font-size-225: 1.125rem; + --wb-font-size-250: 1.25rem; + --wb-font-size-275: 1.375rem; + --wb-font-size-300: 1.5rem; + --wb-font-size-350: 1.75rem; + --wb-font-size-400: 2rem; + } +} diff --git a/packages/ui/src/styles/fonts.css b/packages/ui/src/styles/fonts.css new file mode 100644 index 000000000..076b36ed3 --- /dev/null +++ b/packages/ui/src/styles/fonts.css @@ -0,0 +1,17 @@ +/* Bundled instead of CDN-fetched: strict CSPs, GDPR (pre-consent request), + and air-gapped deployments all rule a CDN out. Loaded via the root barrel + import — consumers importing only subpaths load the families themselves. */ +@import '@fontsource/poppins/latin-300.css' layer(ui.base); +@import '@fontsource/poppins/latin-400.css' layer(ui.base); +@import '@fontsource/poppins/latin-500.css' layer(ui.base); +@import '@fontsource/poppins/latin-600.css' layer(ui.base); +@import '@fontsource/poppins/latin-700.css' layer(ui.base); +@import '@fontsource/poppins/latin-ext-300.css' layer(ui.base); +@import '@fontsource/poppins/latin-ext-400.css' layer(ui.base); +@import '@fontsource/poppins/latin-ext-500.css' layer(ui.base); +@import '@fontsource/poppins/latin-ext-600.css' layer(ui.base); +@import '@fontsource/poppins/latin-ext-700.css' layer(ui.base); + +/* Inter serves the UI/Code type role (wb-text-code), Regular only. */ +@import '@fontsource/inter/latin-400.css' layer(ui.base); +@import '@fontsource/inter/latin-ext-400.css' layer(ui.base); diff --git a/packages/ui/src/styles/typography.css b/packages/ui/src/styles/typography.css index f14dfa579..76a06cacb 100644 --- a/packages/ui/src/styles/typography.css +++ b/packages/ui/src/styles/typography.css @@ -166,4 +166,213 @@ font-size: 0.625rem; line-height: 100%; } + + .wb-text-display-s, + .wb-text-display-m, + .wb-text-display-l, + .wb-text-headline-s, + .wb-text-headline-m, + .wb-text-headline-l, + .wb-text-title-s, + .wb-text-title-m, + .wb-text-title-l, + .wb-text-body-s, + .wb-text-body-m, + .wb-text-body-l, + .wb-text-label-s, + .wb-text-label-m, + .wb-text-label-l, + .wb-text-label-xl, + .wb-text-node-s, + .wb-text-node-m, + .wb-text-node-l, + .wb-text-display-s-emphasized, + .wb-text-display-m-emphasized, + .wb-text-display-l-emphasized, + .wb-text-headline-s-emphasized, + .wb-text-headline-m-emphasized, + .wb-text-headline-l-emphasized, + .wb-text-title-s-emphasized, + .wb-text-title-m-emphasized, + .wb-text-title-l-emphasized, + .wb-text-body-s-emphasized, + .wb-text-body-m-emphasized, + .wb-text-body-l-emphasized, + .wb-text-label-s-emphasized, + .wb-text-label-m-emphasized, + .wb-text-label-l-emphasized, + .wb-text-label-xl-emphasized, + .wb-text-node-s-emphasized, + .wb-text-node-m-emphasized, + .wb-text-node-l-emphasized { + font-family: 'Poppins', system-ui, sans-serif; + } + + .wb-text-code { + font-family: 'Inter', system-ui, sans-serif; + } + + .wb-text-display-s, + .wb-text-display-m, + .wb-text-display-l, + .wb-text-headline-s, + .wb-text-headline-m, + .wb-text-headline-l, + .wb-text-title-s, + .wb-text-title-m, + .wb-text-title-l, + .wb-text-body-s, + .wb-text-body-m, + .wb-text-body-l, + .wb-text-label-s, + .wb-text-label-m, + .wb-text-label-l, + .wb-text-label-xl, + .wb-text-node-s, + .wb-text-node-m, + .wb-text-node-l, + .wb-text-code { + font-weight: 400; + } + + .wb-text-display-s, + .wb-text-display-s-emphasized { + font-size: var(--wb-font-size-300); + line-height: 130%; + } + + .wb-text-display-m, + .wb-text-display-m-emphasized { + font-size: var(--wb-font-size-350); + line-height: 120%; + } + + .wb-text-display-l, + .wb-text-display-l-emphasized { + font-size: var(--wb-font-size-400); + line-height: 120%; + } + + .wb-text-headline-s, + .wb-text-headline-s-emphasized { + font-size: var(--wb-font-size-250); + line-height: 130%; + } + + .wb-text-headline-m, + .wb-text-headline-m-emphasized { + font-size: var(--wb-font-size-275); + line-height: 130%; + } + + .wb-text-headline-l, + .wb-text-headline-l-emphasized { + font-size: var(--wb-font-size-300); + line-height: 130%; + } + + .wb-text-title-s, + .wb-text-title-s-emphasized { + font-size: var(--wb-font-size-175); + line-height: 140%; + } + + .wb-text-title-m, + .wb-text-title-m-emphasized { + font-size: var(--wb-font-size-200); + line-height: 140%; + } + + .wb-text-title-l, + .wb-text-title-l-emphasized { + font-size: var(--wb-font-size-225); + line-height: 130%; + } + + .wb-text-body-s, + .wb-text-body-s-emphasized { + font-size: var(--wb-font-size-150); + line-height: 130%; + } + + .wb-text-body-m, + .wb-text-body-m-emphasized { + font-size: var(--wb-font-size-175); + line-height: 140%; + } + + .wb-text-body-l, + .wb-text-body-l-emphasized { + font-size: var(--wb-font-size-200); + line-height: 140%; + } + + .wb-text-label-s, + .wb-text-label-s-emphasized { + font-size: var(--wb-font-size-125); + line-height: 100%; + } + + .wb-text-label-m, + .wb-text-label-m-emphasized { + font-size: var(--wb-font-size-150); + line-height: 100%; + } + + .wb-text-label-l, + .wb-text-label-l-emphasized { + font-size: var(--wb-font-size-175); + line-height: 100%; + } + + .wb-text-label-xl, + .wb-text-label-xl-emphasized { + font-size: var(--wb-font-size-200); + line-height: 100%; + } + + .wb-text-node-s, + .wb-text-node-s-emphasized { + font-size: var(--wb-font-size-137); + line-height: 130%; + } + + .wb-text-node-m, + .wb-text-node-m-emphasized { + font-size: var(--wb-font-size-162); + line-height: 140%; + } + + .wb-text-node-l, + .wb-text-node-l-emphasized { + font-size: var(--wb-font-size-200); + line-height: 140%; + } + + .wb-text-code { + font-size: var(--wb-font-size-150); + line-height: 130%; + } + + .wb-text-display-s-emphasized, + .wb-text-display-m-emphasized, + .wb-text-display-l-emphasized, + .wb-text-headline-s-emphasized, + .wb-text-headline-m-emphasized, + .wb-text-headline-l-emphasized, + .wb-text-title-s-emphasized, + .wb-text-title-m-emphasized, + .wb-text-title-l-emphasized, + .wb-text-body-s-emphasized, + .wb-text-body-m-emphasized, + .wb-text-body-l-emphasized, + .wb-text-label-s-emphasized, + .wb-text-label-m-emphasized, + .wb-text-label-l-emphasized, + .wb-text-label-xl-emphasized, + .wb-text-node-s-emphasized, + .wb-text-node-m-emphasized, + .wb-text-node-l-emphasized { + font-weight: 600; + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd9df70b3..606e2b08f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -510,9 +510,6 @@ importers: '@cfworker/json-schema': specifier: 4.1.1 version: 4.1.1 - '@fontsource/poppins': - specifier: ^5.2.7 - version: 5.2.7 '@jsonforms/core': specifier: ^3.4.0 version: 3.5.1 @@ -643,6 +640,12 @@ importers: specifier: ^8.5.6 version: 8.5.9(@types/react@19.1.8)(react@19.1.0) devDependencies: + '@fontsource/inter': + specifier: ^5.3.0 + version: 5.3.0 + '@fontsource/poppins': + specifier: ^5.3.0 + version: 5.3.0 '@types/react': specifier: 'catalog:' version: 19.1.8 @@ -1874,8 +1877,11 @@ packages: '@floating-ui/utils@0.2.12': resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} - '@fontsource/poppins@5.2.7': - resolution: {integrity: sha512-6uQyPmseo4FgI97WIhA4yWRlNaoLk4vSDK/PyRwdqqZb5zAEuc+Kunt8JTMcsHYUEGYBtN15SNkMajMdqUSUmg==} + '@fontsource/inter@5.3.0': + resolution: {integrity: sha512-RofMylZmjlJEfELXeNHFWBRcSs75rGU/6bV2S2jfnvv/3rPXPGe0LgUJTklcHZ9lM4OZmAVFhcJPnACfb91A3g==} + + '@fontsource/poppins@5.3.0': + resolution: {integrity: sha512-cms1nM7U6SN8epIV1WMVOV8VsA645aSm3wHfzIQnkk188U341ThbJGSl2Sv54V29gnDbT2arMPfLzvVIIVCceQ==} '@gerrit0/mini-shiki@3.23.0': resolution: {integrity: sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==} @@ -9646,7 +9652,9 @@ snapshots: '@floating-ui/utils@0.2.12': {} - '@fontsource/poppins@5.2.7': {} + '@fontsource/inter@5.3.0': {} + + '@fontsource/poppins@5.3.0': {} '@gerrit0/mini-shiki@3.23.0': dependencies: