@codaco/fresco-ui
v2.9.0
Published
Fresco UI components, styles, and utilities
Readme
@codaco/fresco-ui
Fresco UI components, styles, and utilities. See docs/superpowers/specs/2026-04-29-fresco-ui-package-design.md for the design spec and docs/superpowers/plans/2026-04-29-fresco-ui-package.md for the implementation plan.
Install
pnpm add @codaco/fresco-uiPeer dependencies (you most likely already have these):
{
"@base-ui/react": "^1.4.0",
"@codaco/shared-consts": "5.0.0",
"@codaco/tailwind-config": "^1.0.0-alpha.11",
"immer": "^11.1.4",
"motion": "^12.38.0",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"tailwindcss": "^4.2.4",
"zod": "^4.1.13",
"zustand": "^5.0.12",
}Tailwind / CSS setup
The package assumes the host has a Tailwind v4 build plugin wired up
(@tailwindcss/vite for Vite, @tailwindcss/postcss for Next.js / any
PostCSS pipeline). Each CSS file in the chain owns one concern, and
consumers import them in order:
/* styles/globals.css */
@import "@codaco/tailwind-config/fresco.css"; /* Tailwind v4 + theme + plugins + fonts */
@import "@codaco/fresco-ui/styles.css"; /* @source for fresco-ui's dist */If you also use @codaco/interview, add its CSS file too — see that
package's README:
@import "@codaco/interview/styles.css";Do not also @import "tailwindcss" yourself. That import lives
inside @codaco/tailwind-config/fresco.css; adding it again loads
Tailwind's runtime twice and produces duplicate / conflicting
utilities.
@codaco/fresco-ui/styles.css is intentionally tiny — its only content
is a single @source "./**/*.{js,ts,tsx}" directive that tells
Tailwind v4's class scanner to walk the package's compiled JS in
dist/ so it emits the utility classes the components reference. It
does not re-export the design-system foundation, so the
tailwind-config import is not optional.
@codaco/tailwind-config/fresco.css bundles the default and interview
theme variants together with self-hosted Nunito and Inclusive Sans
woff2 files.
The interview theme is activated by the data-theme-interview attribute. Hosts typically apply it via the <ThemedRegion theme="interview"> component (exported from @codaco/fresco-ui/ThemedRegion), which also wires up a <PortalContainerProvider> so dialogs, popovers, dropdowns, tooltips, toasts, selects, and comboboxes portal into a node inside the themed subtree (and therefore inherit the theme's CSS variables) instead of into document.body.
Containing-block constraint: the <ThemedRegion> element and its ancestors up to <body> must not have transform, filter, perspective, or contain set. Any of these creates a new containing block for fixed-positioned descendants, which would break modal/popover positioning. If you cannot satisfy this constraint, fall back to applying data-theme-interview higher in the tree (e.g. <body>).
Public API surface
The exports field in package.json is an explicit allowlist. Add new entries there when shipping new modules. Internals (collection/store, button-constants, etc.) are intentionally excluded — anything not listed in exports is package-private even if Vite compiles it into dist.
