core-ds-phase1
v0.5.0
Published
Phase 1 demo placeholder — client-neutral core design system, multibrand React component library consumed by Figma Make.
Readme
core-ds-phase1
A client-neutral, multibrand React component library. Brands are runtime data — one seed colour derives a full ramp, with foregrounds computed by APCA contrast rather than assumed — so the same components render correctly under any brand without a rebuild.
React 18 peer dependency · ESM + CJS · types included · one stylesheet.
Phase 1 demo. 17 components, and the package name is a placeholder. The API can change on any
0.xrelease — see CHANGELOG.md.
Install
npm install core-ds-phase1 react react-domImport the stylesheet once, at your app's entry point. It carries the token definitions every component depends on, so nothing renders correctly without it:
import 'core-ds-phase1/styles.css'Use
Wrap your tree in a BrandProvider. Everything inside resolves its colours,
type and spacing from that brand:
import {
BrandProvider,
defineBrand,
ProductHeroPrimary,
ContentBlock,
Button,
} from 'core-ds-phase1'
import 'core-ds-phase1/styles.css'
const brand = defineBrand({
name: 'acme',
seed: '#3B5BDB',
})
export function Page() {
return (
<BrandProvider brand={brand}>
<ProductHeroPrimary
caption="Introducing"
heading="A headline from the manuscript"
body="Supporting copy."
cta={<Button variant="primary">Get started</Button>}
/>
<ContentBlock
variant="light"
heading="A section"
body="Body copy for the section."
/>
</BrandProvider>
)
}Nested providers work, so a single section can run on a different brand.
Brands
defineBrand() takes a seed colour and derives the full ramp at runtime. The
package ships no brand data — adding a brand needs no publish and no
version bump.
const brand = defineBrand({
name: 'acme',
seed: '#3B5BDB', // required
accentSeed: '#00A86B', // optional second hue
fontHeading: 'Poppins',
fontBody: 'Source Sans 3',
logo: undefined, // default: a plain text wordmark, no asset
radiusScale: 1,
tintNeutrals: false,
overrides: {}, // merged last, and re-measured for contrast
})Foregrounds are computed, never assumed white. A seed whose step-500 pair
cannot reach a readable contrast floor throws at defineBrand() rather than
shipping an unreadable button. If an overrides entry is rejected, supply both
halves of the pair:
overrides: { primary: { 500: { background: '#f2f2f2', foreground: '#191919' } } }Three brands ship for testing — coreDefault, testPale (light seed, forces a
dark foreground) and testDark (near-black, must not collapse).
BrandLogo renders the brand name as a wordmark by default. Pass a URL, an SVG
string or a React node for a real asset.
What's in it
Components — Button · Card · ContentBlock · ContentBlockCards ·
ContentGroup · Copy · CTAGroup · HighlightBanner · IcoPlaceholder ·
Icons · Link · LinkWithIcon · ProductFooter · ProductHeader ·
ProductHeroPrimary · Quicklinks · Separator
Layout primitives — Container · Grid · Stack · Cluster
Brand engine — defineBrand · BrandProvider · BrandLogo · useBrand
All exports are named and flat. There are no deep import paths; the only
subpath is core-ds-phase1/styles.css.
Layout
Container owns max-width and page margins — components never set their own.
Grid owns columns and gutters. Responsive props take { xs, sm, md, lg },
and omitted keys inherit downward from xs:
<Container>
<Grid cols={{ xs: 1, sm: 2, lg: 3 }}>{cards}</Grid>
</Container>| Breakpoint | Range | Columns | Gutter | Margin |
|---|---|---|---|---|
| xs | 0–639 | 4 | 16 | 16 |
| sm | 640–1023 | 8 | 16 | 24 |
| md | 1024–1365 | 12 | 24 | 32 |
| lg | 1366+ | 12 | 24 | 75 (content 1216) |
Prose content
Components render running copy — sub-headings, paragraphs with line breaks,
ordered and unordered lists — by accepting ordinary markup. There is no <List>
or <Paragraph> to learn:
<ContentBlock heading="Section">
<p>A paragraph.</p>
<ul><li>List markers pick up the brand colour.</li></ul>
</ContentBlock>Text passed to a component is rendered unaltered — never truncated, reordered or reflowed in a way that changes meaning. The library is built for regulated content where that is a correctness requirement, not a preference.
Accessibility
WCAG 2.2 AA is a build requirement, not a target: every component is keyboard reachable with correct roles and reading order, and derived colour pairs are contrast-checked at brand-definition time.
Docs
Full guidelines — per-component props, valid values, correct and incorrect
usage — live in guidelines/ in the repository, alongside foundations (colour,
typography, spacing, theming) and composition rules.
Building on this library, or picking up the project? See
CONTRIBUTING.md for the build, verification and release
process, and CLAUDE.md for the conventions that govern it.
License
UNLICENSED — see package.json.
