Skip to content

perf(themes): static token CSS + Tailwind v4 export (drop runtime token injection) - #332

Open
DipeshRajoria007 wants to merge 1 commit into
masterfrom
perf/static-token-css
Open

perf(themes): static token CSS + Tailwind v4 export (drop runtime token injection)#332
DipeshRajoria007 wants to merge 1 commit into
masterfrom
perf/static-token-css

Conversation

@DipeshRajoria007

Copy link
Copy Markdown
Contributor

Summary

grauity currently injects every design token at runtime via styled-components createGlobalStyle + extractColorVariables() on mount. This PR makes token delivery zero-runtime and, as a side benefit, exposes the tokens to non-styled-components consumers (e.g. Tailwind).

A small build-time generator (scripts/build-tokens.ts, run in build-lib via tsx) emits the tokens as static CSS from the existing single source of truth (constantGlobalStyle + {light,dark}ThemeConstants), shipped in dist/css through Parcel's staticFiles:

File Contents
dist/css/tokens.css All tokens as plain CSS custom properties — :root primitives; light at :root, .grauity-theme-light; dark at .dark, .grauity-theme-dark
dist/css/grauity-theme.css Tailwind v4 @theme export of the primitives (--color-brand-500, --spacing-16px, --radius-8px, --text-16px, font-* …)

Performance win (grauity)

Consumers can now import the static CSS once and skip the runtime global-style entirely:

import '@newtonschool/grauity/dist/css/tokens.css';
// ...
<GrauityThemeProvider injectGlobalStyle={false}>{app}</GrauityThemeProvider>

This removes the styled-components global-style generation + DOM injection on mount (and the per-render extractColorVariables work) from the critical path → faster FCP and less runtime JS. Components are unaffected because they already read tokens via var(--…), never via the styled-components theme object.

No behaviour change by default

  • injectGlobalStyle defaults to true — existing consumers are unaffected.
  • GlobalStyle is refactored to consume the same shared buildThemeVariables() builder used by the static generator, so runtime output and the shipped CSS cannot drift. Output is functionally identical to before.
  • constantGlobalStyle is moved to its own dependency-free module and re-exported from GlobalStyle for back-compat.

Notes

  • tsx added as a devDependency only (build tooling; not shipped, no runtime impact).
  • Generated CSS is gitignored and produced during npm run build (build-libbuild-tokens).
  • Subpath exports (e.g. @newtonschool/grauity/tokens.css) are intentionally not added here to avoid changing module resolution; that belongs with the tree-shakeable-build PR. Until then, use the dist/css/... path above.

Verification

  • npm run build-tokens → emits tokens.css (442 vars) + grauity-theme.css
  • npm run build-lib → full Parcel build green; both CSS files land in dist/css/
  • npm run extract-typings (tsc) green; ESLint + Prettier clean on changed files

🤖 Generated with Claude Code

grauity injects every design token at runtime via styled-components
createGlobalStyle + extractColorVariables() on mount. This adds a build-time
generator (scripts/build-tokens.ts) that emits the same tokens as static CSS,
shipped in dist/css via Parcel staticFiles:

- dist/css/tokens.css         all tokens as plain CSS custom properties
                              (:root primitives; light at :root/.grauity-theme-light;
                              dark at .dark/.grauity-theme-dark)
- dist/css/grauity-theme.css  Tailwind v4 @theme export of the primitives

Consumers can import tokens.css once and pass
<GrauityThemeProvider injectGlobalStyle={false}> to drop the styled-components
global-style generation/injection from the critical path (faster FCP, less
runtime JS). Default behaviour is unchanged (injectGlobalStyle defaults to true).

GlobalStyle is refactored to consume the same shared buildThemeVariables()
builder, so the runtime output and the static CSS can never drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MihirSachdeva added a commit that referenced this pull request Jun 16, 2026
…SSR fix (#336)

## Summary
Consolidated **performance** release of grauity, cut as
**`3.4.0-beta.2`** and validated against real consumer apps. This is the
integration branch combining the four perf PRs **plus** the fixes found
during beta testing, merged up to date with `master`.

## What's in it

**Perf suite (PRs #332#335):**
- **#332** — static token CSS + Tailwind `@theme` export; drops runtime
`createGlobalStyle`/`extractColorVariables` injection (`./tokens.css`,
`./theme.css`).
- **#333** — tree-shakeable per-module **tsup** build (replaces Parcel)
+ `exports` map + precise `sideEffects` + code-splitting.
- **#334** — framer-motion **`LazyMotion`** + `m` + `domAnimation` (~12
KB gz/animated component).
- **#335** — **lodash → lodash-es** (tree-shaken) + inline Icon utils
(~68 KB raw / ~13 KB gz off dist).

**Fixes found during beta testing (the reason for beta.1/beta.2):**
- **SSR crash fix** — switched all 46 source files from `import styled`
(default) to the **named** `import { styled }`. styled-components v6's
*default* export is `undefined` under Node's native ESM loader (which
Next.js uses during "Collecting page data"), so the previous beta
crashed consumer builds (`styled.i.attrs` of undefined). The named
import works under Node-ESM, CJS, and bundlers — no `node`-condition
workaround needed.
- **Barrel tree-shaking fix** — tsup entry glob `index.ts` →
`index.{ts,tsx}`, so the 9 `.tsx` components (PopOver, SelectDropdown,
Tooltip, DropdownMenu, RangeInput, FloatingActionButton,
Form/{Dropdown,FormRow,TextArea}) get their own chunks instead of being
inlined into the barrel. Result: `import { NSButton } from
'@newtonschool/grauity'` drops framer-motion + yup — **71.5 KB → 14.3 KB
gz** — so barrel consumers get the tree-shaking win with **no import
changes**.

**Merged up to `master`:** includes the transient-props
styled-components refactor (#327), `3.3.13`, and the new icons (#330,
#331). The `Modal.{tsx,styles.ts}` conflicts were resolved by keeping
master's transient-props refactor **and** the LazyMotion/named-import
changes.

**Build fix:** added the `@rollup/rollup-linux-x64-gnu` install entry to
`package-lock.json` (the lock had been regenerated on macOS, dropping
the Linux rollup binary → the publish CI's `npm ci` failed on `tsup`;
npm/cli#4828).

## Verification (no regressions)
- `tsc --noEmit`, `eslint`, `prettier --check`, **`jest` 46 suites / 412
tests**, `tsup` build — all green on the merged tree.
- Node native-ESM `import()` of the barrel + subpaths resolves (SSR
crash gone); browser tree-shaking preserved.
- **Real consumers on `3.4.0-beta.1`:** newton-web (`next build` —
258/258 pages, the page that crashed on beta.0 now builds) and
newton-school-public-website — **both CI green**.

## Publishing notes
- Publish under the **`beta`** dist-tag — the workflow's `npm-publish`
defaults to `latest`, which would clobber `3.3.13` for all consumers.
After publishing, run `npm dist-tag add @newtonschool/grauity@3.3.13
latest` to restore stable.
- Source PRs #332#335 remain open; this PR is the consolidated beta.
The team decides whether to merge this combined PR or land them
individually for the stable `3.4.0`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Mihir Sachdeva <mihirsachdeva.ms@gmail.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