Skip to content

feat(tokens): sync espresso v2 from Figma + rebuild foundations docs#727

Open
netchampfaris wants to merge 22 commits into
mainfrom
v1/espresso-tokens
Open

feat(tokens): sync espresso v2 from Figma + rebuild foundations docs#727
netchampfaris wants to merge 22 commits into
mainfrom
v1/espresso-tokens

Conversation

@netchampfaris
Copy link
Copy Markdown
Contributor

@netchampfaris netchampfaris commented May 21, 2026

Summary

  • Pipes the new espresso v2 Figma export (W3C DTCG format) into the Tailwind preset via a generator script (yarn sync-tokens). Refreshes tailwind/colors.json (new *.950 shades, gray-alpha / red-alpha ramps, semantic surface/ink/outline) and replaces hardcoded borderRadius/fontSize in plugin.js with the generated JSON.
  • Adds rounded-0..rounded-9 numeric radii (incl. 5px / 6px previously only reachable via arbitrary values) alongside the existing named aliases, plus typography sizes up to text-15xl.
  • Rebuilds the design system docs as a Foundations section: Colours — Base, Colours — Semantic, Typography, Corner Radius, Drop Shadow. New pages live at /docs/foundations/* and read directly from the generated JSON.
  • Keeps legacy semantic tokens dropped by the Figma export alive (surface-cards, surface-white, ink-gray-9, outline-gray-modals, etc.) via alias + legacy-entry maps in the generator — no churn for existing src/ or docs/ usage.

Files of interest

  • tailwind/figma-tokens-to-theme.js — the generator
  • tailwind/colors.json, tailwind/generated/{radius,typography}.json — synced outputs
  • tailwind/plugin.js — now consumes JSON instead of hardcoded scales
  • docs/components/foundations/*.vue — new docs pages with light/dark TabButtons toggle + click-to-copy
  • docs/components/Layout.vue — custom OnThisPage now respects outline: false frontmatter

Test plan

  • yarn sync-tokens regenerates JSON without errors
  • yarn build passes
  • yarn test passes (39/39 locally)
  • /docs/foundations/colours/base — palette renders, light/dark toggle swaps values
  • /docs/foundations/colours/semantic — surface/ink/outline tokens grouped by subfamily, 4-column responsive grid
  • /docs/foundations/corner-radius — both numeric and named tile grids visible
  • /docs/foundations/typography — text + display sizes render with metadata
  • Home page still renders (surface-cards legacy alias)
  • Existing components using bg-surface-*, text-ink-*, border-outline-*, rounded-* look unchanged in light mode

Docs preview: https://ui.frappe.io/pr-preview/pr-727/

Coverage: 48.92% (±0.00% vs main)

netchampfaris and others added 22 commits May 27, 2026 16:49
…ndations docs

Generator
- tailwind/figma-tokens-to-theme.js reads the W3C DTCG token JSON
  exported from Figma (espresso-v2-design-tokens/) and emits
  tailwind/generated/{colors,radius,typography}.json
- Run with `yarn sync-tokens`
- Preserves legacy semantic names (surface-cards, surface-white,
  ink-gray-9, outline-gray-modals, …) via alias + legacy-entry maps
  so existing src/ and docs/ usage keeps working through the rename

Tailwind
- tailwind/colors.json refreshed from Figma; adds *.950 shades,
  gray-alpha and red-alpha ramps, surface/ink/outline updates
- tailwind/plugin.js now consumes radius.json + typography.json
  instead of hardcoded scales; exposes new sizes (tiny, 4xl..15xl)
  and the full numeric radius scale (rounded-0..9) alongside the
  named aliases

Docs
- New /docs/foundations/* pages replacing /docs/design-system/*:
  Colours — Base, Colours — Semantic, Typography, Corner Radius,
  Drop Shadow
- Components in docs/components/foundations/ read directly from
  the generated JSON; light/dark TabButtons toggle; click-to-copy
- Layout.vue respects `outline: false` frontmatter (custom
  OnThisPage was previously unconditional)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
transformerStyleToClass only flushes its CSS at buildEnd, so in dev the
generated shiki.css stays empty and code blocks lose their syntax
highlighting. Skip the transformer in dev and let shiki emit inline
--shiki-light/dark styles directly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add text-{size}-medium utilities (ADR-0004) for Figma's named
  typography styles; Button md/lg adopt them for exact tracking parity
- Switch Button focus ring to ring-2 to match Figma's 2px shadow (ADR-0005)
- Adopt numbered radius tokens as canonical (ADR-0006); Button migrates
  from named aliases; named aliases flagged for migration
- Add spec/foundations.md covering typography, focus, radius, themes,
  code-only extensions, and the Figma verification process

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Delete Variants/Themes/Sizes/Icons stories; the API table already
  covers prop variations
- Add 6 contextual stories matching the Button Figma file: section
  controls, section action, selection toolbar, inline actions, stacked
  actions, and a live-class card
- Add a Playground builder (TabButtons + Switch knobs) with live
  preview and dynamically generated Vue snippet; registered globally
  as <ButtonBuilder> in the VitePress theme

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wire the Figma token pipeline to emit elevation/focus effects, expose
them as CSS variables, and register `shadow-*` / `ring-*` utilities
that resolve to the new variables. Replace the standalone drop-shadow
docs page with a broader elevation page that previews both stacks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Consolidates the lucide / feather / emoji / Vue-component icon-rendering
pattern that's currently duplicated across Button, Dropdown, Dialog, and
OptionIcon into one small component, so new call sites have one obvious
choice. Falsy values render nothing; fallthrough attrs go to the
rendered root.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
A compact tab item used by TabButtons (and reusable on its own). The
`icon` prop renders an icon-only pill — any `label` is exposed only to
assistive tech. `iconLeft` / `iconRight` are accent icons rendered next
to a visible label, matching Button's semantics. Variants cover the
segmented, outline, underline, and browser-tab looks. Icon rendering
goes through the shared `<Icon>` component.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…wrap

TabButtons no longer wraps `<Button>` internally — each tab is now a
native `<button>`, `<a href>`, or `<RouterLink>` rendering a `<Pill>`
for its visual treatment.

BREAKING CHANGE: per-option `theme`, `variant`, `size`, `loading`,
`hideLabel` and `tooltip`-as-popover are no longer honored. Use the
shared `<Button>` directly if any of these matter, or supply richer
content via the `prefix` / `suffix` slots. Migration notes added to
`TabButtons.md`.

New: `route` and `href` per option render the tab as a RouterLink or
anchor respectively. The internal v-model fallback chain
(`value ?? label ?? index`) is preserved for back-compat with consumers
that bound to a label string.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Picks up the new Pill and Icon components in the auto-resolved
declaration file, drops stale Rating story entries, and absorbs
incidental type-union reordering in Divider/Popover/Tooltip api docs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the four duplicated *Builder.vue files (~180 lines each, 90%
identical chrome) with a shared ComponentPlayground that takes a knob
schema + #preview slot + code(values) fn. Each per-component builder
shrinks to ~30-80 lines of pure config.

Code preview matches story preview visuals exactly: runtime shiki
highlighting with the same tokyo-night/github-light themes, .shiki
sizing and bg, and the global .copy button style.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Previously the active indicator was a 3px rounded bar floating 3px
outboard of the rail, with full pill height. Read as a text caret next
to the label rather than a tab marker.

Now: 1px-wide square segment positioned at right:-2px (overlapping the
column rail), label-height via inset-y-1.5. Active label gets
font-medium on underline variant so the label itself signals selection.
Adds pr-2 to vertical underline pills so short labels don't sit flush
against the indicator.

Also cleans up a stray </content> tag at the end of Pill.vue and
TabButtons.vue (left over from prior commits, broke SFC parsing).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Swap the per-site focus-visible:ring-* + ring-outline-gray-* patterns
for the new focus-ring / focus-ring-{theme} utilities backed by the
--focus-* design tokens. Button maps theme → themed focus-ring
(blue/green/red); other components use the default gray focus-ring.

Triggers in Select / shared selection / MultiSelect TagsTrigger also
adopt focus-ring on data-[state=open] so the open ring matches the
focus ring exactly. Intentional suppression sites (Sidebar item, ghost
input variants) left unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace generic Tab/Tab placeholder options with Home/Task/Contact/Others
nav and an icon-only ghost sidebar, inspired by the Figma showcase. Use
distinct iconLeft icons per option and switch the Sizes / PrefixSuffix
demos to view-switcher and Inbox/Comments patterns.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The docs render the stories/ snippets directly; this orphan Histoire-style
file isn't referenced anywhere.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Sidebar/page renames: "Colours — Base" → "Base Colors", "Colours —
  Semantic" → "Semantic Colors", "Corner Radius" → "Radius" (file
  renamed to foundations/radius.md, URL paths for colours unchanged).
- SemanticPage rewritten as a flat row list per token: swatch, name,
  → reference, and resolved value — no surrounding box, no hover.
- PalettePage: drop the per-swatch inset 1px ring and the p-2 wrappers
  around the overlay sections; each overlay swatch now layers its
  translucent color over its own bg-ink-gray-9 / bg-surface-white
  backdrop via a linear-gradient.
- New docs/composables/useTheme.ts watches the <html data-theme>
  attribute (set by Navbar.vue) so the local Light/Dark switchers on
  both foundations pages follow global theme toggles in real time.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Espresso 2.0's Figma dark-mode page applies elevation/light/* shadows
universally — never the elevation/dark/* set. Match that: emit
--elevation-{sm..2xl} from :root only, so shadow-* resolves to the same
value in both themes. The Figma dark set is still exposed as
--dark-elevation-* / shadow-dark-* for opt-in heavier shadows.

Pill: raised active state now uses bg-surface-gray-2-contrast shadow-base
+ text-ink-gray-8, matching the Figma dark-mode active pill (lighter
surface over container, subtle light shadow with inner highlight, less
glaring label).

TabButtons: clip the container so the active pill's shadow doesn't
overflow the rounded corners.

Docs: elevation page copy reflects the single-shadow-set model;
ElevationPreview switcher uses TabButtons and syncs with the global
theme toggle (same pattern as Palette/Semantic pages).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pass defaultColor: false to Shiki's codeToHtml so both themes are emitted
as CSS variables (--shiki-light / --shiki-dark) instead of inline color
on the light theme. The [data-theme="dark"] .shiki span rule in
docs/css/style.css can then flip tokens at runtime — previously the
light theme's inline color won by specificity and snippets stayed light
even in dark mode.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Radius: collapse the numeric and named-alias sections into one divided
list (one row per scale step). Each row shows the rounded-N class +
--radius-N CSS variable, with aliases like `rounded`, `rounded-sm`
rendered as inline chips on the numeric row they map to. Swatches
enlarged so adjacent steps (e.g. 2 → 3 = 5px → 6px) are distinguishable.

Plumb --radius-* CSS variables through tailwind/plugin.js so the docs
aren't lying: borderRadius now resolves via var(--radius-N), with
`DEFAULT` reusing the numeric var that shares its value. Values
unchanged.

Typography: display line-height as a unitless ratio (lh / size) instead
of the raw px value from Figma — closer to how designers reason about
leading.
…values

PR #720 on main extracted borderRadius/boxShadow/fontSize into tokens.js
as a public sub-path export. Our branch already replaced those values
with Figma-generated tokens in plugin.js. After the rebase, tokens.js
still exported the legacy hardcoded values, drifting from what the
plugin actually emits. Source from ./generated/* so external consumers
match the plugin output.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant