@pure-ds/core
v0.4.8
Published
Pure Design System - Why develop a Design System when you can generate one?
Downloads
3,009
Maintainers
Readme
Pure Design System (PDS)
⚠️ Beta Software - APIs are stabilizing but may still change. Pin versions in production:
"@pure-ds/core": "~0.3.0"

With Great Standards Comes Great Power
The browser is the framework. Semantic HTML is the component model. Web Standards are enough.
PDS is a configuration-first, standards-only design system generator. Not a framework. Not a CSS library. Not tied to any toolchain.
You write a small JavaScript config. PDS generates:
- Deterministic CSS (global and Constructable Stylesheets for Web Components)
- A complete token hierarchy (inspect via
PDS.compiledin DevTools) - Zero-specificity primitives (
:where()selectors—your CSS always wins)
Everything is optional. Use only tokens, or CSS, or add enhancements, or include components. Nothing forces itself into your project.
The Config
// pds.config.js
export const config = {
design: {
colors: { primary: '#007acc', secondary: '#5c2d91' },
typography: { baseFontSize: 16, fontScale: 1.25 },
spatialRhythm: { baseUnit: 8, scaleRatio: 1.5 }
}
}The Result
// app.js
import { PDS } from '@pure-ds/core';
import { config } from './pds.config.js';
await PDS.start(config);
// That's it. Start writing semantic HTML.The PDS Philosophy
Semantic Classes First
PDS generates high-level primitives that style semantic HTML:
<article class="card">...</article>
<button class="btn-primary">Save</button>
<div class="alert alert-success">Done!</div>Layout Utilities—Sparingly
A small set of layout utilities for composition:
<div class="flex gap-md items-center">...</div>
<section class="stack-lg">...</section>No .text-blue-500. No .p-4. No .rounded-lg. Spacing, colors, radii are tokens—not classes.
Inline Styles? Only for Tokens
The only valid style="" in PDS sets CSS custom properties:
<!-- ✓ Token override -->
<section style="--surface-bg: var(--color-primary-50);">
<!-- ✗ Never do this -->
<div style="display: flex; gap: 16px;">Why PDS Exists
| The Old Way | The PDS Way |
|-------------|-------------|
| class="flex items-center gap-4 p-6 bg-white rounded-lg shadow-md" | class="card" |
| style="color: #007acc;" | Uses --color-primary-500 token |
| Import a Button component | <button class="btn-primary"> |
| 47 utility classes per element | Semantic class + maybe one layout utility |
The result: Readable HTML. Inspectable CSS. Sites that work without JS. Code that lasts decades.
PDS follows the Pure Web Manifesto—sustainable architecture for long-lived applications.
Key Features
- 🎨 Configuration-Driven — Single source of truth generates everything
- 🚀 Live or Static — Runtime generation or pre-built bundles
- 🎯 Framework Agnostic — Vanilla, Lit, React, Vue, Svelte, Next.js
- 🌐 Web Standards — EventTarget API, Constructable Stylesheets, Shadow DOM
- 🧩 Progressive Enhancement — Semantic HTML first, enhance where needed
- ♿ Accessibility Built-in — WCAG AA validation, contrast checking
- 📦 Zero Build Required — Works directly in browsers
- 📋 IDE IntelliSense — Full autocomplete via Custom Elements Manifest
Table of Contents
- The Three Layers
- Who is it For?
- Getting Started
- Core Architecture
- Styling Layers
- Shadow DOM Adoption
- Icon System
- Smart Query System
- Design Validation
- Advanced Features
- API Reference
- Extending PDS
- Using from CDN
- CLI & Export
- Custom Elements Manifest
- Framework Integration
- Troubleshooting
- License
The Three Layers
PDS is built on three fully optional layers, each powered by your config:
1. Styles — Deterministic Global CSS
Your config generates:
- Color scales (50–900 from base colors)
- Surface semantics (bg, text, border, shadow, states)
- Spacing system (mathematical progression)
- Typography scale (modular scale from base)
- Primitives (
.btn-primary,.card,.badge) - Utilities (
.flex,.gap-md,.stack-lg)
All exported as CSS Custom Properties. Zero specificity via :where().
Same values available in JS via PDS.compiled.tokens.
2. Enhancements — Semantic HTML Made Powerful
Optional selector-based upgrades that run in Light DOM and Shadow DOM:
- Required fields show markers and help text automatically
<label data-toggle>becomes a switch<nav data-dropdown>gets dropdown behavior<dialog>gets better focus management- Form elements gain consistent theming
Think: HTML → UX upgrades, zero integration work.
3. Components — Auto-defined, Lazy-loaded Web Components
A growing set of PDS components:
<pds-icon>— SVG sprite icons<pds-drawer>— Slide-out panels<pds-tabstrip>— Accessible tabs<pds-jsonform>— Forms from JSON Schema<pds-upload>— Drag & drop file upload<pds-richtext>— Rich text / Markdown editor<pds-splitpanel>— Resizable panes<pds-toaster>— Toast notifications
Auto-defined when used. Lazy-loaded via dynamic imports. Styled by your tokens. Zero dependencies.
How It Works
┌──────────────────────────────────────────────────────────────┐
│ Configuration │
│ ───────────── │
│ colors: { primary: '#007acc' } │
│ typography: { baseFontSize: 16, fontScale: 1.25 } │
│ spatialRhythm: { baseUnit: 8 } │
└─────────────────────┬────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Generator (Live or Static) │
│ ────────────────────────── │
│ • Generates color scales (primary-50 → primary-900) │
│ • Creates surface tokens (bg, text, border, shadow) │
│ • Computes spacing progression (0-12) │
│ • Builds typography scale (6 levels) │
│ • Generates CSS layers (tokens → primitives → utilities) │
│ • Validates contrast ratios (WCAG AA) │
└─────────────────────┬────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Output │
│ ────── │
│ Tokens: --color-primary-500, --spacing-4 │
│ Primitives: .btn-primary, .card, .badge │
│ Components: <pds-drawer>, <pds-icon> │
│ Utilities: .flex, .gap-md, .border-gradient │
└──────────────────────────────────────────────────────────────┘Who is it For?
🎨 Design System Teams
- Generate multiple brand variants from a single codebase
- A/B test design directions by swapping configs
- Maintain consistency with centralized tokens
- Validate accessibility automatically
🚀 Product Teams
- Ship faster with auto-generated primitives
- Customize with JS config instead of SASS/CSS
- Work with any framework or no framework
- Reduce bundle size with lazy-loaded components
🛠️ Design Tool Builders
- Build live configurators with instant preview
- Export static bundles for production
- Query the system programmatically
- Access complete object model for introspection
👩💻 Developers
- Learn by exploring the interactive showcase
- Adopt incrementally (tokens only → full system)
- Extend with custom enhancers and components
- Debug with structured CSS layers
Getting Started
Installation
npm install @pure-ds/coreAssets (components, icons) are auto-copied to your project during install. To manually sync:
node node_modules/@pure-ds/core/packages/pds-cli/bin/postinstall.jsLit Import Convention
PDS uses a virtual import for Lit so you control the resolution:
import { html, css, LitElement } from '#pds/lit';In browsers (no bundler) - Use import maps:
<script type="importmap">
{
"imports": {
"#pds/lit": "/assets/js/lit.js"
}
}
</script>In bundlers - Alias to the real lit package:
// vite.config.js
export default {
resolve: {
alias: { '#pds/lit': 'lit' }
}
}Quick Start: Live Mode
Generate styles at runtime with instant updates:
import { PDS } from '@pure-ds/core';
await PDS.start({
mode: 'live',
preset: 'default', // or: ocean-breeze, midnight-steel, etc.
// Override preset values
design: {
colors: {
primary: '#007acc',
secondary: '#5c2d91'
},
typography: {
fontFamilyHeadings: 'Inter, sans-serif',
fontFamilyBody: 'Inter, sans-serif'
}
},
// Component auto-loading
autoDefine: {
predefine: ['pds-icon'] // Eagerly load these
}
});
// Use components - they'll lazy-load automatically
// <pds-drawer id="menu"></pds-drawer>Quick Start: Static Mode
Pre-generate assets for production:
1. Export static files:
npm run pds:exportThis creates:
pds/styles/pds-*.cssandpds-*.css.js(Constructable Stylesheets)pds/components/*.js(Web Components)pds/icons/pds-icons.svg(Icon sprite)pds/custom-elements.json(Custom Elements Manifest for IDE integration)
2. Initialize in static mode:
import { PDS } from '@pure-ds/core';
await PDS.start({
mode: 'static',
preset: 'default',
staticPaths: {
tokens: '/pds/styles/pds-tokens.css.js',
primitives: '/pds/styles/pds-primitives.css.js',
components: '/pds/styles/pds-components.css.js',
utilities: '/pds/styles/pds-utilities.css.js',
styles: '/pds/styles/pds-styles.css.js'
},
});Minimal Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>PDS App</title>
<script type="importmap">
{
"imports": {
"#pds/lit": "/assets/js/lit.js"
}
}
</script>
</head>
<body>
<button class="btn-primary">
<pds-icon icon="heart"></pds-icon>
Click me
</button>
<script type="module">
import { PDS } from '/assets/js/pds.js';
await PDS.start({
design: {
colors: { primary: '#007acc' }
}
});
</script>
</body>
</html>Core Architecture
PDS has three integrated systems that work together to create complete design systems.
1. Style Generation & Injection
The heart of PDS is the Generator - it transforms your configuration into structured CSS.
Token Generation
// Your config
{
colors: { primary: '#007acc' },
typography: { baseFontSize: 16, fontScale: 1.25 },
spatialRhythm: { baseUnit: 8, scaleRatio: 1.5 }
}
// PDS generates tokens
--color-primary-50 → #e6f7ff
--color-primary-100 → #b3e5ff
--color-primary-200 → #80d4ff
...
--color-primary-900 → #003d66
--spacing-0 → 0
--spacing-1 → 8px
--spacing-2 → 12px (8 × 1.5)
--spacing-3 → 18px (12 × 1.5)
...
--font-size-xs → 0.64rem
--font-size-sm → 0.8rem
--font-size-base → 1rem
--font-size-lg → 1.25rem
--font-size-xl → 1.5625remSurface Semantics
PDS generates smart surface tokens that adapt to context:
/* Light theme */
--surface-bg: var(--color-gray-50);
--surface-text: var(--color-gray-900);
--surface-text-secondary: var(--color-gray-700);
--surface-border: var(--color-gray-300);
--surface-shadow: rgba(0, 0, 0, 0.1);
/* Dark theme (auto-generated) */
[data-theme="dark"] {
--surface-bg: var(--color-gray-900);
--surface-text: var(--color-gray-50);
--surface-text-secondary: var(--color-gray-300);
--surface-border: var(--color-gray-700);
--surface-shadow: rgba(0, 0, 0, 0.5);
}Interactive States
Button and interactive element states are computed automatically:
/* Primary button */
--primary-fill: var(--color-primary-600);
--primary-fill-hover: var(--color-primary-700);
--primary-fill-active: var(--color-primary-800);
/* Text/outline buttons */
--primary-text: var(--color-primary-600);
--primary-text-hover: var(--color-primary-700);Live vs Static Mode
Live Mode:
- CSS generated in-browser at runtime
- Instant updates when config changes
- Perfect for design tools and configurators
- Access via
PDS.compiledobject model - Automatic font loading from Google Fonts
Static Mode:
- CSS pre-generated at build time
- Optimized for production performance
- Host anywhere (CDN, static server)
- Constructable Stylesheets for instant adoption
- No runtime overhead
2. Progressive Enhancements
Lightweight JavaScript behaviors applied to semantic HTML. These are not Web Components - just DOM enhancements that make standard HTML more interactive.
Built-in Enhancers
Dropdown Menus - <nav data-dropdown>
<nav data-dropdown>
<button>Menu</button>
<menu>
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</menu>
</nav>Features:
- Click to toggle visibility
- Auto-positioning (up/down based on space)
- Horizontal alignment (
.align-rightclass) - Keyboard support (Escape to close)
- Click-outside to close
- Scrollable when content exceeds viewport
Toggle Switches - <label data-toggle>
<label data-toggle>
<span data-label>Enable notifications</span>
<input type="checkbox">
</label>Creates styled toggle switches from standard checkboxes.
Range Sliders - <input type="range">
<!-- Standard mode: floating bubble on interaction -->
<label>
<span>Volume</span>
<input type="range" min="0" max="100" value="50">
</label>
<!-- Inline output mode: persistent value display -->
<label class="range-output">
<span>Volume</span>
<input type="range" min="0" max="100" value="50">
</label>Enhances range inputs with automatic value display. Use range-output class for inline output with semantic <output> element.
Required Field Indicators - form [required]
<label>
<span>Email</span>
<input type="email" required>
</label>Automatically adds asterisk to label.
Custom Enhancers
Add your own progressive enhancements:
await PDS.start({
enhancers: [
{
selector: '[data-tooltip]',
description: 'Adds tooltip on hover',
run: (element) => {
const text = element.dataset.tooltip;
element.addEventListener('mouseenter', () => {
// Show tooltip
});
}
},
{
selector: '[data-copy]',
description: 'Copy text to clipboard on click',
run: (element) => {
element.addEventListener('click', () => {
navigator.clipboard.writeText(element.dataset.copy);
});
}
}
]
});3. Web Components
Rich, reusable UI components built with Lit. Lazy-loaded automatically via the Auto-Define system.
Available Components
<pds-icon> - SVG sprite icons
<pds-icon icon="heart"></pds-icon>
<pds-icon icon="star" size="lg"></pds-icon>
<pds-icon icon="list" size="32" color="red"></pds-icon>
<pds-icon icon="info" label="Information"></pds-icon>Attributes:
icon- Symbol ID (required)size- Named size (xs/sm/md/lg/xl/2xl) or pixel valuecolor- CSS color (defaults to currentColor)label- Accessible name (makes iconrole="img")
<pds-drawer> - Slide-out panels
<pds-drawer id="menu" position="left">
<h2 slot="header">Menu</h2>
<nav>...</nav>
</pds-drawer>
<button onclick="document.getElementById('menu').open()">
Open Menu
</button>Attributes:
position- left/right/top/bottomopen- Boolean, controls visibility
Methods:
open()- Show drawerclose()- Hide drawertoggle()- Toggle visibility
<pds-tabstrip> - Accessible tab interface
<pds-tabstrip>
<button slot="tab">Overview</button>
<div slot="panel">Overview content</div>
<button slot="tab">Details</button>
<div slot="panel">Details content</div>
</pds-tabstrip>Features:
- Keyboard navigation (Arrow keys, Home, End)
- ARIA attributes automatic
- Focus management
- URL hash sync (optional)
<pds-upload> - File upload with preview
<pds-upload
accept="image/*"
multiple
max-size="5000000">
</pds-upload>Attributes:
accept- File types (MIME or extensions)multiple- Allow multiple filesmax-size- Max file size in bytesmax-files- Max number of files
Events:
files-changed- Fired when selection changesfile-error- Fired on validation errors
<pds-toaster> - Toast notifications
<pds-toaster id="toaster"></pds-toaster>
<script>
const toaster = document.getElementById('toaster');
toaster.show({
message: 'Saved successfully!',
type: 'success',
duration: 3000
});
</script>Or use via events:
PDS.dispatchEvent(new CustomEvent('pds:toast', {
detail: {
message: 'Error occurred',
type: 'danger',
duration: 5000
}
}));<pds-richtext> - Rich text editor
<script type="importmap">
{
"imports": {
"#showdown": "https://cdn.jsdelivr.net/npm/[email protected]/dist/showdown.mjs"
}
}
</script>
<pds-richtext
value="<p>Initial content</p>"
format="html">
</pds-richtext>Set format="markdown" when you want the submitted form value to stay in Markdown:
<pds-richtext name="release-notes" format="markdown"></pds-richtext>The element will use the #showdown import-map specifier first, and will only fall back to loading the script from CDNs if that specifier is missing.
Features:
- Bold, italic, underline, strikethrough
- Headings, lists, links
- Code blocks
- Undo/redo
- Markdown shortcuts
<pds-jsonform> - Dynamic forms from JSON Schema
<pds-jsonform schema='{"type":"object","properties":{...}}'></pds-jsonform>Generates complete forms with validation from JSON Schema.
<pds-splitpanel> - Resizable panes
<pds-splitpanel orientation="horizontal">
<div slot="start">Left pane</div>
<div slot="end">Right pane</div>
</pds-splitpanel><pds-scrollrow> - Horizontal scrolling container
<pds-scrollrow>
<div class="card">Card 1</div>
<div class="card">Card 2</div>
<div class="card">Card 3</div>
</pds-scrollrow>Shows scroll buttons when content overflows.
Auto-Define System
Components are registered automatically when their tags appear in the DOM:
await PDS.start({
autoDefine: {
baseURL: '/auto-define/',
// Eagerly load these components
predefine: ['pds-icon', 'pds-drawer'],
// Custom file mapping
mapper: (tag) => {
if (tag.startsWith('my-')) {
return `/components/${tag}.js`;
}
// Return nothing to use PDS default mapping
},
// Advanced options
scanExisting: true, // Scan for tags on init
observeShadows: true, // Watch Shadow DOM
patchAttachShadow: true, // Monitor dynamic shadows
debounceMs: 16 // Debounce observation
}
});The system:
- Observes DOM for new custom elements
- Checks if tag is defined
- Loads corresponding module
- Registers custom element
- Upgrades existing instances
⚠️ Important: Programmatic Access to Auto-Defined Components
Components loaded via
autoDefineare registered asynchronously. If you need to access component methods or properties programmatically (e.g.,toaster.toast()), ensure the component is defined first:// Wait for component to be defined await customElements.whenDefined('pds-toaster'); const toaster = document.querySelector('pds-toaster'); toaster.toast('Hello!');Alternative: Use
predefineto eagerly load components atPDS.start()time:await PDS.start({ autoDefine: { predefine: ['pds-toaster', 'pds-icon'] // Loaded immediately } }); // Components are now available synchronously
Styling Layers
PDS generates CSS in structured layers for predictable specificity and modularity.
Layer Structure
┌──────────────────────────────────────────────────────────┐
│ tokens CSS Custom Properties │
│ --color-*, --spacing-*, --font-* │
├──────────────────────────────────────────────────────────┤
│ primitives Base elements │
│ button, input, .card, .badge │
├──────────────────────────────────────────────────────────┤
│ components Rich UI │
│ .drawer, .tabstrip, .upload │
├──────────────────────────────────────────────────────────┤
│ utilities Composable helpers │
│ .flex, .gap-4, .border-gradient │
└──────────────────────────────────────────────────────────┘Layer Details
Tokens - Design foundation
- Colors (scales + semantics)
- Spacing (0-12 progression)
- Typography (families, sizes, weights)
- Borders (widths, radius)
- Shadows (depths)
- Transitions (speeds)
- Z-index (layers)
- Layout (breakpoints, max-width)
Primitives - Native elements enhanced
- Buttons (
.btn-primary,.btn-secondary,.btn-outline) - Forms (
input,select,textarea,fieldset) - Surfaces (
.card,.surface) - Badges (
.badge,.badge-success) - Alerts (
.alert,.alert-warning) - Typography (headings, paragraphs, lists)
- Tables (responsive, striped)
Components - Web Component styles
- Styles for
<pds-*>elements - Internal component structure
- State management (
:state()selectors)
Utilities - Layout and effects
- Flex (
.flex,.flex-col,.items-center) - Grid (
.grid,.grid-cols-3) - Spacing (
.gap-4,.p-4,.m-2) - Borders (
.border,.border-gradient,.border-glow) - Effects (
.shadow-lg,.rounded-lg)
Accessing Layers
In live mode:
const compiled = PDS.compiled;
// Get layer CSS
const tokensCSS = compiled.layers.tokens.css;
const primitivesCSS = compiled.layers.primitives.css;
// Get as stylesheet
const sheet = await PDS.registry.getStylesheet('primitives');In static mode:
// Import constructable stylesheets
import tokensSheet from '/pds/styles/pds-tokens.css.js';
import primitivesSheet from '/pds/styles/pds-primitives.css.js';Shadow DOM Adoption
PDS provides helpers for adopting styles into Shadow DOM.
Basic Adoption
class MyComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
async connectedCallback() {
// Adopt just primitives (most common)
await PDS.adoptPrimitives(this.shadowRoot);
this.shadowRoot.innerHTML = `
<button class="btn-primary">Click me</button>
`;
}
}Multiple Layers
// Adopt specific layers
await PDS.adoptLayers(this.shadowRoot, ['primitives', 'components']);
// Adopt with custom styles
const myStyles = PDS.createStylesheet(`
:host {
display: block;
padding: var(--spacing-4);
}
`);
await PDS.adoptLayers(this.shadowRoot, ['primitives'], [myStyles]);Lit Component Example
import { html, css, LitElement } from '#pds/lit';
class MyCard extends LitElement {
static styles = css`
:host {
display: block;
}
`;
async connectedCallback() {
super.connectedCallback();
await PDS.adoptPrimitives(this.shadowRoot);
}
render() {
return html`
<article class="card">
<h3>Card Title</h3>
<p>Card content</p>
<button class="btn-primary">Action</button>
</article>
`;
}
}Icon System
PDS uses SVG sprites for efficient icon rendering.
Icon Component
<!-- Basic usage -->
<pds-icon icon="heart"></pds-icon>
<!-- Sized icons -->
<pds-icon icon="star" size="sm"></pds-icon>
<pds-icon icon="star" size="lg"></pds-icon>
<pds-icon icon="star" size="32"></pds-icon>
<!-- Colored icons -->
<pds-icon icon="warning" color="red"></pds-icon>
<pds-icon icon="info" color="var(--color-primary-500)"></pds-icon>
<!-- Accessible icons -->
<pds-icon icon="list" label="Open navigation menu"></pds-icon>Configuration
await PDS.start({
design: {
icons: {
set: 'phosphor', // Icon family
weight: 'regular', // Icon weight
defaultSize: 24, // Default size in pixels
sizes: { // Named sizes
xs: 16,
sm: 20,
md: 24,
lg: 32,
xl: 48,
'2xl': 64
},
spritePath: '/assets/pds/icons/pds-icons.svg'
}
}
});Custom Icons
Add custom icons to the sprite:
1. Configure custom icons:
// pds.config.js
export default {
design: {
icons: {
set: 'phosphor',
include: [
'house', 'gear', 'heart', 'star',
'user', 'bell', 'search', 'menu'
]
}
}
};2. Rebuild sprite:
npm run pds:build-icons3. Use in your app:
<pds-icon icon="house"></pds-icon>Icon Tokens
Icons are available as CSS custom properties:
--icon-set: phosphor;
--icon-weight: regular;
--icon-size-xs: 16px;
--icon-size-sm: 20px;
--icon-size-md: 24px;
--icon-size-lg: 32px;Smart Query System
Ask questions about your design system using natural language.
Usage
// Programmatic API
const results = await PDS.query("what is the focus border color on inputs?");
results.forEach(result => {
console.log(result.text); // "Focus border color: var(--color-primary-500)"
console.log(result.category); // "Color Token"
console.log(result.cssVar); // "var(--color-primary-500)"
console.log(result.code); // Example code
});Example Queries
Color Questions:
await PDS.query("what is the focus border color on inputs?")
await PDS.query("what foreground color should I use on this surface?")
await PDS.query("button hover color")
await PDS.query("primary color scale")Utility Questions:
await PDS.query("what are the utility classes for borders?")
await PDS.query("border gradient effect")
await PDS.query("flex layout utilities")
await PDS.query("gap between elements")Component Questions:
await PDS.query("how do I create an icon-only button?")
await PDS.query("drawer component")
await PDS.query("tab strip usage")Layout Questions:
await PDS.query("how can I group stuff in containers?")
await PDS.query("grid container")
await PDS.query("card component")AutoComplete Integration
The query system integrates with #pds-search in the configurator. Type queries directly in the search box for instant answers.
How It Works
- Intent Detection - Recognizes what you're asking about (color, spacing, component, utility)
- Entity Recognition - Identifies design elements (button, input, surface)
- Context Analysis - Detects states (hover, focus, active)
- Data Querying - Searches
PDS.compiled,PDS.ontology,PDS.currentConfig - Scoring & Ranking - Returns top 10 most relevant results
See PDS-QUERY-SYSTEM.md for detailed documentation.
Design Validation
PDS automatically validates designs for accessibility issues.
Automatic Validation
In live mode with presets enabled:
npm run buildValidates all presets during build and reports issues:
❌ Preset validation failed:
— Travel Market
• Primary text on surface is too low (3.95 < 4.5)
[light/outline] (/colors/primary)
— Mobility App
• Primary button contrast too low in dark theme (2.85 < 4.5)
[dark/btn-primary] (/colors/darkMode/primary)Manual Validation
const result = PDS.validateDesign({
colors: {
primary: '#007acc',
background: '#ffffff'
}
}, {
minContrast: 4.5 // WCAG AA standard
});
if (!result.ok) {
console.table(result.issues);
// [
// {
// path: '/colors/primary',
// message: 'Primary button contrast too low...',
// ratio: 3.2,
// min: 4.5,
// context: 'light/btn-primary'
// }
// ]
}Batch Validation
const results = PDS.validateDesigns([
{ name: 'Light', config: {...} },
{ name: 'Dark', config: {...} }
], {
minContrast: 4.5
});
results.forEach(({ name, ok, issues }) => {
if (!ok) {
console.log(`${name} has ${issues.length} issues`);
}
});Validation Checks
- Primary Button (Light) - Button fill vs white text
- Primary Button (Dark) - Button fill vs white text in dark mode
- Surface Text (Light) - Text color vs surface background
- Primary Links/Outline (Light) - Primary text vs surface
- Surface Text (Dark) - Text color vs dark surface
All checks verify WCAG AA minimum contrast ratio (4.5:1 default).
Advanced Features
Automatic Font Loading
In live mode, PDS automatically loads fonts from Google Fonts when they're not available locally.
await PDS.start({
mode: 'live',
design: {
typography: {
fontFamilyHeadings: 'Inter, sans-serif',
fontFamilyBody: 'Inter, sans-serif',
fontFamilyMono: 'Fira Code, monospace'
}
}
});
// Fonts are automatically loaded from Google Fonts if neededFeatures:
- Smart detection (skips system fonts)
- Parallel loading for performance
- Font weights: 400, 500, 600, 700
font-display: swapfor better UX- 5-second timeout prevents hanging
Manual loading:
import { loadGoogleFont } from '@pure-ds/core/common/font-loader';
await loadGoogleFont('Roboto', {
weights: [400, 500, 700],
italic: true
});Event Bus
PDS is an EventTarget - listen for system events:
// System ready
PDS.addEventListener('pds:ready', (e) => {
console.log('PDS ready:', e.detail.mode);
});
// Theme changed
PDS.addEventListener('pds:theme:changed', (e) => {
console.log('Theme:', e.detail.theme);
});
// Design updated (configurator)
PDS.addEventListener('pds:design:updated', (e) => {
console.log('New config:', e.detail.config);
});
// Error handling
PDS.addEventListener('pds:error', (e) => {
console.error('PDS error:', e.detail.error);
});Available events:
pds:ready- System initializedpds:error- Error occurredpds:theme:changed- Theme switchedpds:design:updated- Config changedpds:design:field:changed- Single field updatedpds:inspector:mode:changed- Inspector toggledpds:inspector:deactivate- Inspector close requestedpds:docs:view- Documentation view requestedpds:toast- Toast notification triggered
Theme Management
// Get current theme
const theme = PDS.theme; // 'light' | 'dark' | 'system' | null
// Set theme
PDS.theme = 'dark';
PDS.theme = 'system'; // Follows OS preference
PDS.theme = null; // Remove preference
// Or use method
await PDS.setTheme('dark');Theme is stored in localStorage and updates html[data-theme] automatically.
Compiled Object Model
In live mode, access the complete generated system:
const compiled = PDS.compiled;
// Tokens
compiled.tokens.colors.primary[500];
compiled.tokens.spacing[4];
compiled.tokens.typography.fontFamily.body;
// Layers
compiled.layers.tokens.css;
compiled.layers.primitives.css;
// Metadata
compiled.meta.generatedAt;
compiled.meta.totalSize;
compiled.meta.tokenGroups;
// Helpers
compiled.helpers.getColorScales();
compiled.helpers.getColorScale('primary');
compiled.helpers.getSpacingValues();Presets
Choose from built-in presets:
// View available presets
Object.keys(PDS.presets);
// ['default', 'ocean-breeze', 'midnight-steel', ...]
// Use a preset
await PDS.start({
preset: 'ocean-breeze',
design: {
// Override specific values
colors: { primary: '#custom' }
}
});
// Access preset config
const preset = PDS.presets['ocean-breeze'];
console.log(preset.colors.primary);Available presets:
default- Clean, modern baselineocean-breeze- Cool blues and tealsmidnight-steel- Dark, professionalsunset-vibes- Warm oranges and purplesforest-calm- Natural greenslavender-dream- Soft purplescoral-energy- Vibrant pinks and orangesarctic-frost- Cool grays and bluesgolden-hour- Warm yellows and goldsneon-city- Bright, high-contrasttravel-market- Earthy, adventurousmobility-app- Tech-forward transportation
API Reference
PDS.start(config)
Main initialization method.
await PDS.start({
// Mode
mode?: 'live' | 'static' = 'live',
// Design configuration
preset?: string,
design?: {
colors?: {...},
typography?: {...},
spatialRhythm?: {...},
shape?: {...},
behavior?: {...},
layout?: {...},
layers?: {...},
icons?: {...}
},
// Static mode paths
staticPaths?: {
tokens?: string,
primitives?: string,
components?: string,
utilities?: string,
styles?: string
},
// Component loading
autoDefine?: {
baseURL?: string,
predefine?: string[],
mapper?: (tag: string) => string | void,
scanExisting?: boolean,
observeShadows?: boolean,
patchAttachShadow?: boolean,
debounceMs?: number
},
// Progressive enhancements
enhancers?: Array<{
selector: string,
description?: string,
run: (element: Element) => void
}>,
// Runtime options
applyGlobalStyles?: boolean = false,
manageTheme?: boolean = false,
themeStorageKey?: string = 'pure-ds-theme',
preloadStyles?: boolean = false,
criticalLayers?: string[] = ['tokens', 'primitives']
});PDS.query(question)
Smart query interface.
const results = await PDS.query(question: string);
// Returns array of results with text, value, icon, category, etc.PDS.validateDesign(config, options)
Validate design for accessibility.
const result = PDS.validateDesign(config, { minContrast: 4.5 });
// Returns: { ok: boolean, issues: Array }PDS.adoptLayers(shadowRoot, layers, additionalSheets)
Adopt stylesheets into Shadow DOM.
await PDS.adoptLayers(shadowRoot, ['primitives'], [customSheet]);PDS.adoptPrimitives(shadowRoot, additionalSheets)
Adopt primitives layer (convenience method).
PDS.createStylesheet(css)
Create constructable stylesheet from CSS string.
PDS.setTheme(theme)
Change theme programmatically.
PDS Properties
PDS.Generator // Generator class
PDS.registry // Runtime registry
PDS.ontology // Design system metadata
PDS.presets // Built-in presets
PDS.enums // Enumeration values
PDS.currentConfig // Current configuration (read-only)
PDS.compiled // Compiled state (live mode only)
PDS.theme // Current theme (getter/setter)
PDS.defaultEnhancers // Built-in enhancementsExtending PDS
Custom Enhancers
await PDS.start({
enhancers: [
{
selector: '[data-animate-in]',
description: 'Animate elements as they enter viewport',
run: (element) => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
element.classList.add('animated');
observer.unobserve(element);
}
});
});
observer.observe(element);
}
}
]
});Custom Components
await PDS.start({
autoDefine: {
mapper: (tag) => {
if (tag.startsWith('my-')) {
return `/components/${tag}.js`;
}
// Let PDS handle pds-* components
}
}
});Custom Presets
export const myPreset = {
id: 'my-brand',
name: 'My Brand Theme',
colors: { primary: '#007acc', secondary: '#5c2d91' },
typography: { fontFamilyHeadings: 'Montserrat' }
};
await PDS.start({ design: myPreset });Using from CDN
<!DOCTYPE html>
<html lang="en">
<head>
<script type="importmap">
{
"imports": {
"#pds/lit": "https://cdn.jsdelivr.net/npm/lit@3/index.js"
}
}
</script>
</head>
<body>
<button class="btn-primary">Click me</button>
<script type="module">
import { PDS } from 'https://cdn.jsdelivr.net/npm/@pure-ds/core@latest/public/assets/js/pds.js';
await PDS.start({
mode: 'static',
staticPaths: {
tokens: 'https://cdn.jsdelivr.net/npm/@pure-ds/core@latest/public/assets/pds/styles/pds-tokens.css.js',
primitives: 'https://cdn.jsdelivr.net/npm/@pure-ds/core@latest/public/assets/pds/styles/pds-primitives.css.js',
utilities: 'https://cdn.jsdelivr.net/npm/@pure-ds/core@latest/public/assets/pds/styles/pds-utilities.css.js'
},
autoDefine: {
baseURL: 'https://cdn.jsdelivr.net/npm/@pure-ds/core@latest/public/assets/pds/components/'
},
applyGlobalStyles: true
});
</script>
</body>
</html>CLI & Export
Available Scripts
| Script | Description |
|--------|-------------|
| npm run pds:export | Full export: styles, components, icons, and IntelliSense data |
| npm run pds:dx | Generate all IntelliSense data (HTML + CSS) |
| npm run pds:manifest | Generate HTML IntelliSense (Custom Elements Manifest) |
| npm run pds:css-data | Generate CSS IntelliSense (tokens, classes, attributes) |
| npm run pds:build-icons | Build custom icon sprite |
| npm run sync-assets | Sync assets between locations |
Export Static Assets
npm run pds:exportOutput:
pds/
├── styles/
│ ├── pds-tokens.css
│ ├── pds-tokens.css.js
│ ├── pds-primitives.css
│ ├── pds-primitives.css.js
│ ├── pds-components.css
│ ├── pds-components.css.js
│ ├── pds-utilities.css
│ ├── pds-utilities.css.js
│ └── pds-styles.css.js
├── components/
│ └── pds-*.js (all components)
├── icons/
│ └── pds-icons.svg
├── custom-elements.json # HTML IntelliSense
├── vscode-custom-data.json # HTML IntelliSense (VS Code)
├── pds.css-data.json # CSS IntelliSense (VS Code)
└── pds-css-complete.json # CSS IntelliSense (all editors)Generate IntelliSense Data
For complete IDE support with autocomplete:
# Generate both HTML and CSS IntelliSense (recommended)
npm run pds:dx
# Or generate individually
npm run pds:manifest # HTML component autocomplete
npm run pds:css-data # CSS token & class autocompleteSee INTELLISENSE.md for setup instructions.
Configuration
// pds.config.js
export default {
staticBase: 'pds',
static: { root: 'public/assets/pds/' },
preset: 'default',
design: { colors: { primary: '#007acc' } }
};Build Icons
npm run pds:build-iconsSync Assets
npm run sync-assetsIntelliSense & IDE Support
PDS provides comprehensive IntelliSense support for both HTML and CSS, dramatically improving developer experience with autocomplete, documentation, and type hints.
📖 Full IntelliSense Guide - Detailed setup for all editors
Quick Setup (VS Code)
Add to .vscode/settings.json:
{
"html.customData": [
"node_modules/@pure-ds/core/public/assets/pds/vscode-custom-data.json"
],
"css.customData": [
"node_modules/@pure-ds/core/public/assets/pds/pds.css-data.json"
]
}Reload VS Code: Ctrl+Shift+P → Developer: Reload Window
What You Get
HTML IntelliSense
- ✅ Web component autocomplete (
<pds-drawer>,<pds-icon>) - ✅ Attribute suggestions with descriptions
- ✅ Enum value autocomplete (
position="left|right|top|bottom") - ✅ Icon name suggestions (all available icons)
CSS IntelliSense
- ✅ CSS token autocomplete in
.cssfiles and<style>tags - ✅ Token value previews on hover (
--color-primary-500,--spacing-4) - ✅ 165 CSS custom properties with descriptions
- ⚠️ Note: Inline
styleattributes don't support IntelliSense (VS Code limitation - use CSS files or<style>tags)
📖 CSS IntelliSense Limitations - Important info about where IntelliSense works
Generation
IntelliSense data is automatically generated with export:
# Generate all IntelliSense data (HTML + CSS)
npm run pds:dx
# Or as part of full export
npm run pds:export
# Or generate individually
npm run pds:manifest # HTML IntelliSense only
npm run pds:css-data # CSS IntelliSense onlyGenerated Files
public/assets/pds/
├── custom-elements.json # Standard Custom Elements Manifest
├── vscode-custom-data.json # VS Code HTML custom data
├── pds.css-data.json # VS Code CSS custom data
└── pds-css-complete.json # Standard CSS data (all editors)
# Root reference files
pds.html-data.json # Points to HTML custom data
pds.css-data.json # Points to CSS custom dataUsage Examples
HTML Autocomplete:
<!-- Type <pds- to see all components -->
<pds-drawer position="right" open>
<div slot="drawer-header">Settings</div>
</pds-drawer>
<!-- Icon autocomplete suggests all available icons -->
<pds-icon icon="star"></pds-icon>CSS Token Autocomplete:
.my-component {
/* Type --color and see all color tokens */
background: var(--color-primary-500);
padding: var(--spacing-4);
border-radius: var(--radius-md);
box-shadow: var(--shadow-lg);
}Utility Class Autocomplete:
<div class="flex gap-4 items-center">
<div class="card surface-elevated">
<!-- Primitives and utilities autocomplete -->
</div>
</div>What's Included
150+ CSS Custom Properties:
- Colors:
--color-{name}-{50-900} - Spacing:
--spacing-{xs|sm|md|lg|xl|...} - Typography:
--font-family-*,--font-size-*,--font-weight-* - Borders:
--radius-*,--border-width-* - Shadows:
--shadow-{sm|md|lg|xl|2xl} - Surfaces:
--surface-bg,--surface-text,--surface-border
50+ CSS Classes:
- Primitives:
.badge,.card,.surface,.alert - Layout:
.flex,.grid,.grid-cols-{1-6},.container - Utilities:
.gap-{0-12},.items-*,.justify-* - Effects:
.border-gradient,.border-glow
5+ Data Enhancements:
data-dropdown,data-toggle,data-tabs,data-modal,data-tooltip
Cross-Editor Support
PDS IntelliSense works with:
- ✅ VS Code - Full support (HTML + CSS)
- ✅ WebStorm/IntelliJ - Automatic recognition
- ✅ Sublime Text - Via LSP package
- ✅ Vim/Neovim - Via coc-css/coc-html
- ✅ Any LSP-compliant editor
See INTELLISENSE.md for detailed setup instructions for each editor.
Custom Elements Manifest
PDS automatically generates a Custom Elements Manifest for web component documentation. This is part of the IntelliSense system but can be used standalone.
What's Documented
- Properties, attributes, methods, events
- Slots and CSS custom properties
- CSS parts for Shadow DOM styling
See the IntelliSense Guide for complete documentation.
- 📖 Hover documentation - View descriptions without leaving your code
For detailed information, see CUSTOM-ELEMENTS-MANIFEST.md.
Framework Integration
Vite
// vite.config.js
export default {
resolve: {
alias: { '#pds/lit': 'lit' }
}
};
// main.js
import { PDS } from '@pure-ds/core';
await PDS.start({ design: { colors: { primary: '#007acc' } } });Next.js
// app/layout.tsx
'use client';
import { useEffect } from 'react';
import { PDS } from '@pure-ds/core';
export default function RootLayout({ children }) {
useEffect(() => {
PDS.start({ mode: 'static', applyGlobalStyles: true });
}, []);
return <html><body>{children}</body></html>;
}
// next.config.js
module.exports = {
webpack: (config) => {
config.resolve.alias['#pds/lit'] = 'lit';
return config;
}
};React
import { useEffect } from 'react';
import { PDS } from 'pure-ds';
function App() {
useEffect(() => {
PDS.start({ design: { colors: { primary: '#007acc' } } });
}, []);
return <button className="btn-primary">Click me</button>;
}Vue
// main.js
import { PDS } from 'pure-ds';
await PDS.start({ design: { colors: { primary: '#007acc' } } });
// vite.config.js
export default {
resolve: { alias: { '#pds/lit': 'lit' } }
};Troubleshooting
Components Not Loading
- Verify components directory exists
- Check import map for
#pds/lit - Manually sync:
node node_modules/pure-ds/packages/pds-cli/bin/postinstall.js - Check browser console for errors
Flash of Unstyled Content
await PDS.start({
preloadStyles: true,
criticalLayers: ['tokens', 'primitives']
});Theme Not Changing
Enable theme management:
await PDS.start({ manageTheme: true });
await PDS.setTheme('dark');Fonts Not Loading
Check font names are correct:
design: {
typography: {
fontFamilyHeadings: 'Inter, sans-serif' // Exact name
}
}Contributing
Contributions welcome! See CONTRIBUTING.md.
git clone https://github.com/mvneerven/pure-ds.git
cd pure-ds
npm install
npm run devLicense
ISC License - See LICENSE
Links
- 🌐 Homepage: https://puredesignsystem.z6.web.core.windows.net/
- 📦 NPM: https://www.npmjs.com/package/pure-ds
- 🐙 GitHub: https://github.com/mvneerven/pure-ds
- 📖 Docs: GETTING-STARTED.md | PDS-QUERY-SYSTEM.md
- 💬 Discussions: https://github.com/mvneerven/pure-ds/discussions
- 🐛 Issues: https://github.com/mvneerven/pure-ds/issues
Made with ❤️ for the open web
