@incodetech/prizma-core-web
v0.11.4
Published
Prizma Design System — React (web) component library.
Maintainers
Keywords
Readme
@incodetech/prizma-core-web
Prizma Design System — React (web) component library. Compiled ESM + generated
declarations, React kept external. Tokens live in
@incodetech/prizma-core-tokens; icons in
@incodetech/prizma-core-icons.
Installing styles (the CSS contract)
Prizma components no longer import any CSS from their JavaScript. The consuming
application owns every style import, so you control what ships, in what order,
and in which cascade layer. JavaScript modules stay tree-shakeable; CSS is the
package's only declared side effect (sideEffects: ["**/*.css"]).
Import the tokens once, from the token package:
@import '@incodetech/prizma-core-tokens/css';Then choose your component styles. Either the aggregate (every component):
@import '@incodetech/prizma-core-web/styles.css';…or only the components you use (per-component subpaths, kebab-case):
@import '@incodetech/prizma-core-web/button.css';
@import '@incodetech/prizma-core-web/card-list.css';
@import '@incodetech/prizma-core-web/id-capture.css';Both models are supported. The aggregate is the union of every per-component sheet — importing it plus a per-component sheet is harmless (idempotent), just redundant.
Cascade layers
Because the imports are explicit, you own the cascade order. Wrap the imports in layers to keep application styles authoritative over Prizma without specificity wars:
@layer prizma-tokens, prizma-components, app-theme;
@import '@incodetech/prizma-core-tokens/css' layer(prizma-tokens);
@import '@incodetech/prizma-core-web/button.css' layer(prizma-components);
@import './app-theme.css' layer(app-theme);The package ships no !important — winning the cascade is the consumer's to
own, not the library's.
Public CSS boundaries
Stable root classes (public)
Every component renders a single BEM block class on its root element. These are stable public API — safe to target for layout, placement, and test selection:
.prizma-button .prizma-checkbox .prizma-radio
.prizma-radio-group .prizma-toggle .prizma-dropdown
.prizma-input-textfield .prizma-input-multiline .prizma-input-otp
.prizma-input-combined .prizma-input-password .prizma-input-datefield
.prizma-button-group .prizma-inline-actions .prizma-card
.prizma-card-list-* .prizma-list .prizma-modal
.prizma-modal-presenter .prizma-bottom-sheet .prizma-bottom-sheet-presenter
.prizma-snackbar .prizma-tag .prizma-title
.prizma-tooltip .prizma-stepper .prizma-timer
.prizma-separator .prizma-disclaimer .prizma-logo
.prizma-nav .prizma-loading-bar .prizma-loading-spinner
.prizma-signature-pad .prizma-id-capture .prizma-selfie-capture
.prizma-capture-* .prizma-illustration-*Prefer the className prop over targeting these directly — it lands on the root
element.
Custom properties (public)
Component visuals are driven entirely by --prizma-* custom properties from the
token package. These are the visual-customization API. See the
tokens README for the three token levels
(primitive / semantic / component) and which are safe for customer branding —
the short version:
- Primitive (
--prizma-color-*, raw scale): source of truth, do not override. - Semantic (
--prizma-surface-*,--prizma-text-*,--prizma-border-*): the recommended layer for application themes. - Component (
--prizma-button-*,--prizma-input-*, …): targeted per-component branding.
Customize by re-declaring semantic or component properties in your own theme scope — you never patch a Prizma selector to change a color, radius, or spacing.
Private (internal)
Everything below the root block is private and unstable — subject to change without notice. Do not target:
- Element selectors:
.prizma-button__label,.prizma-button__spinner,.prizma-input-textfield__field, and any other__-suffixed selector. - State/variant modifier classes (
--size-l,--state-hover, …): the component applies these from its props; drive state through props, not by adding classes.
/* ✗ don't — couples you to private markup */
.prizma-button .prizma-button__label { font-weight: 700; }
/* ✓ do — layout on the root via className, visuals via tokens */
.my-form-actions .prizma-button { margin-inline-start: auto; }Layout vs. tokens — the split
| Need | Use |
| --- | --- |
| Placement: grid/flex position, width constraints, margins, integration positioning, test hooks | className on the root |
| Visuals: color, typography, borders, radius, spacing, motion | Prizma tokens (custom properties) |
The style exports contain only component styles scoped under the
.prizma-* block classes — no CSS resets and no bare global element selectors.
Importing a component sheet will not restyle the rest of your application.
Theme activation
Prizma defines token names and default (light) values and consumes semantic
variables; the application decides which theme is active and where it is
scoped. Optional namespaced presets ship from the token package
(@incodetech/prizma-core-tokens/themes/light.css / themes/dark.css, activated
by [data-prizma-theme='light'|'dark']), or map your own theme's variables onto
--prizma-* in your theme scope. Full details in the
tokens README.
React Server Components (Next.js App Router)
Every component module in dist/ opens with 'use client', so Core components
import straight into an App Router page with no wrapper. The directive is
applied by the build to the src/*.tsx modules only — applyPrizmaTheme,
derivePrizmaTheme and the type modules stay server-importable, and the build
fails if the directive ever lands on one of them.
src/ is untouched, so Vite-alias consumers (the hub, Core Lab) see no change.
One thing the consumer still owns: @incodetech/prizma-core-icons ships raw
TypeScript source, so a Next app needs
transpilePackages: ['@incodetech/prizma-core-icons'] in next.config.
Maintainer notes
src/styles.css, the package exports map, and the CSS subpaths are all
generated from the files in src/ by scripts/exports-map.mjs (single source of
truth). npm run build regenerates and drift-checks both the exports map and
the aggregate stylesheet, so adding a component (Foo.tsx + Foo.css) and
running the build is enough — a new CSS file can never silently miss the
aggregate or the subpath export. Regenerate by hand with:
node scripts/exports-map.mjs # → package.json "exports"
node scripts/exports-map.mjs --styles # → src/styles.css