@designmatters/ui
v0.1.20
Published
Design Matters internal pixel-art inspired Nuxt UI component library
Readme
Design Matters UI
Design Matters UI is our internal Nuxt module for building a cohesive component library with a modern yet nostalgic pixel-art aesthetic. The goal is to keep the experience clean, approachable, and consistent across all Design Matters products.
Philosophy
- Pixel art inspired look with modern accessibility and readability standards.
- Components ship with thoughtful defaults and allow opt-in overrides where it makes sense.
- Documentation-first approach: every change updates Storybook docs, the README, and supporting markdown logs.
- Test-driven development to guarantee quality and regression safety.
Repository Layout
src/the Nuxt module entry point and runtime hooks that will power the library.playground/a Nuxt sandbox for exploring component ideas before sharing them internally.test/Vitest + Nuxt Test Utils configuration for TDD and regression coverage.docs/knowledge base capturing context, design decisions, component inventory, specs, and status updates.docs/design-tokens.mdcentralises the colour palette, typography scale, and spacing system..github/automation guidelines, including Copilot instructions that reference all documentation.
Documentation Workflow
- Capture team-wide context in
docs/context.md. - Record significant design decisions in
docs/design-decisions.md. - Track progress and recent changes in
docs/progress-log.md. - Maintain the evolving component inventory in
docs/component-inventory.mdand mirror it in Storybook. - Define and update the foundation tokens in
docs/design-tokens.mdalongsidesrc/runtime/tokens.ts. - Draft and refine component specifications under
docs/specs/before writing code. - See
docs/storybook/for how we document UI components using Storybook (temporary markdown mirror until live Storybook instance).
Whenever you work on the library, update the relevant docs and cross-reference them from pull requests. Copilot is configured to remind contributors to keep everything in sync.
Installation (External Projects)
Install (after the first publish):
npm install @designmatters/ui
# or: pnpm add @designmatters/ui / bun add @designmatters/uiRegister the module in nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@designmatters/ui'],
// Optional future overrides
// designmattersUi: { tokens: { /* partial token overrides */ } }
})Use components directly (auto‑registered with Dm prefix):
<template>
<DmButton variant="primary">Click Me</DmButton>
</template>Access tokens (advanced / future):
const tokens = inject('designmatters:tokens')Vue (peer) and Nuxt (peer) versions must satisfy the ranges in
peerDependencies.
Publishing & Release Flow
Prerequisites:
- Run tests & lint:
npm run lint && npm run test - Build locally:
npm run prepack(generatesdist/used by publish) - Inspect the package:
npm pack --dry-run(optional sanity check)
First-time publish (scoped public package):
npm login # if not already
npm version 0.1.0 # or use changelogen release automation
npm publish --access publicSubsequent releases (uses the existing release script):
npm run releaseThis performs (in order): lint → tests → build (prepack) → changelog/tag (changelogen) → publish → push tags.
Manual version bump without changelogen:
npm version patch # or minor | major
npm publish
git push --follow-tagsVerification in a fresh app (recommended smoke test):
npx nuxi init demo-app
cd demo-app
npm install @designmatters/ui
echo "<template><DmButton>Hi</DmButton></template>" > app.vue
npx nuxi devIf the button renders and no peer warnings appear, the package is healthy.
Common Publish Issues
| Symptom | Cause | Fix |
|---------|-------|-----|
| Component not found in consumer | Build not run / missing dist | Run npm run prepack before publish |
| Duplicate Vue instance warnings | Linked locally with mismatched Vue version | Ensure consumer and library share compatible Vue peer version |
| Tokens undefined | Injection happens before plugin ran | Confirm plugin included (module not registered or build incomplete) |
| Types missing | types.d.mts excluded | Verify it exists in dist/ and exports map |
Versioning Strategy
Use 0.x while APIs are unstable. Move to 1.0.0 only after: Button + Typography + Form Inputs baseline + token override API finalized.
Development Workflow
- Install dependencies with
npm installand generate stubs withnpm run dev:prepare. - Use
npm run devto iterate in the playground and document results in Storybook. - Follow TDD: add or update a test in
test/, runnpm run test, and only then implement or adjust components. - Keep linting green with
npm run lint. - Release steps are captured in the
npm run releasescript once the library is production-ready.
What is a Fixture?
In our tests, a fixture is a minimal, isolated Nuxt application used to exercise the module in a realistic consumer setting. Each fixture lives under test/fixtures/<name> and usually contains:
nuxt.config.ts– registers the module (@designmatters/ui) and any scenario-specific config.package.json– only if the scenario needs extra dependencies (most stay minimal and inherit root tooling).pages/directory (recommended) orapp.vue– actual routes/pages the tests will request via$fetch.- Optional additional files (plugins, middleware) to reproduce edge cases.
Benefits:
- Decouples test cases from the playground and global root config.
- Mirrors how external apps install and consume the module (ensuring auto component registration works).
- Produces deterministic SSR HTML for assertions.
If you see 500 errors in SSR tests, first verify the fixture has a pages/index.vue (Nuxt can rely on app.vue, but an explicit page avoids some blank-render edge cases in test mode).
Creating a New Component (Start → Finish)
This is the authoritative lifecycle for shipping a new Dm* component.
1. Plan & Spec
| Artifact | Purpose |
|----------|---------|
| docs/specs/<component>.md | Source of truth: problem, audience, props, states, variants, accessibility notes, open questions. |
| docs/component-inventory.md | Add / update status (e.g. Draft → In Progress → Beta → Stable). |
| docs/design-decisions.md | Record trade‑offs (performance vs. API simplicity, naming, theming choices). |
Checklist:
- Identify required design tokens (reuse first; introduce new only with justification and update
tokens.ts+docs/design-tokens.md). - Define naming (PascalCase after
Dmprefix) and minimal initial scope.
2. Test-Driven Kickoff
- Add a placeholder failure either in a dedicated test (
test/dm-<component>.e2e.test.ts) or temporarily extendcomponent-roadmap.test.ts. - For rendered output, plan an SSR test using a fixture and
$fetchassertions.
3. Scaffold Fixture
Create test/fixtures/<component>/ with:
test/fixtures/<component>/
nuxt.config.ts # registers the module
pages/index.vue # simplest/default variant
pages/variants.vue # (optional) other variants/states
pages/states.vue # (optional) loading/disabled/error etc.Add only what you need—keep fixtures lean.
4. Implement the Component
- File:
src/runtime/components/Dm<Component>.vue(auto-registered viaaddComponentsDir). - Use
script setup+ strong TypeScript props & emits. - Inject tokens:
const tokens = inject(DESIGN_TOKENS_KEY, fallbackTokens). - Expose accessible markup (semantic element, ARIA attributes, focus styles).
- Style via CSS custom properties sourced from tokens (avoid raw hex unless documented new token).
5. Tests (Layers)
| Layer | Tool | Goal |
|-------|------|------|
| SSR/E2E | @nuxt/test-utils + Vitest | Ensure component renders correctly in a Nuxt app (HTML, classes, variants). |
| Unit (optional) | Vitest | Pure logic/composables, prop normalization, edge cases. |
| Types (optional) | npm run test:types | Ensure public API types do not regress. |
Add assertions for:
- Default render (base classes, structure, accessible name).
- Each variant / state class.
- Token-driven style hooks (at least presence of CSS class names or data attributes, not pixel values).
- Interactive or disabled behaviours (prevent click, show loading indicator, etc.).
6. Documentation & Storybook
Create / update docs/storybook/components/<component>.md (markdown source feeding Storybook stories) including:
- Summary & intent.
- Import and usage snippet.
- Props table (name, type, default, description, a11y impact).
- Variants matrix.
- Accessibility section (keyboard, ARIA, focus, color contrast assumptions).
- Design rationale / anti-patterns.
CRITICAL: When creating Storybook stories (.stories.ts files):
- Always include
parameters.docs.source.codefor every story to show accurate code examples - Code examples must include slot content and realistic prop values (not
v-bind="args") - See
docs/storybook-story-guidelines.mdfor complete requirements - Use
stories/_template.stories.tsas a starting point for new components
Cross‑sync:
- Progress log entry in
docs/progress-log.md. - If you created or changed tokens, mirror them in
docs/design-tokens.md.
7. Accessibility Verification
- Keyboard nav (Tab / Shift+Tab / Enter / Space / Arrow keys as needed).
- Screen reader accessible name / role.
- Focus outline visibility and contrast.
- Reduced motion / animations (provide fallbacks if added later).
8. Distribution Readiness
- Run
npm run prepack(build + types). Confirm:dist/module.mjsexists and exports the auto components.dist/types.d.mtsincludes your component’s prop types.
- (Optional) Smoke test in the playground: import or just use
<Dm<Component>>(auto-registration) inplayground/app.vue.
9. Finalize Tests
- Replace any
notImplementedplaceholder with real assertions. - Ensure
npm run testpasses (excluding intentionally deferred roadmap items—convert them toit.todoif still pending).
10. Definition of Done
- ✅ All related fixture routes return 200 and contain expected HTML.
- ✅ No unresolved TODOs or placeholder failures for the component.
- ✅ Docs (spec + Storybook + inventory + decision log + progress log) are current.
- ✅ Types & build output validated via
npm run prepack. - ✅ Accessibility considerations addressed or explicitly documented.
Local Development: Run, View, Test
Install & Prepare
npm install
npm run dev:prepare # builds stub module + prepares playgroundPlayground (Visual Manual Testing)
npm run dev # launches Nuxt playgroundOpen the printed local URL (usually http://localhost:3000). Add component usages in playground/app.vue or create pages inside playground/.
Viewing Storybook / Documentation
Until interactive Storybook is wired, markdown lives under docs/storybook/. After Storybook setup, each component will have:
- MDX story file (usage + controls)
- Visual regression target (future)
- Linked markdown spec
Running Tests
npm run test # one-off, CI mode
npm run test:watch # watch mode during development
npm run test:types # type-level API & TS declaration checksCommon Troubleshooting
| Issue | Fix |
|-------|-----|
| Component not found | Ensure filename is Dm<Name>.vue and inside src/runtime/components. |
| SSR 500 in fixture | Confirm fixture has pages/index.vue; check injection key usage & build logs. |
| Styles missing | Verify CSS lives inside the SFC or imported; re-run dev:prepare. |
| Types missing after build | Run npm run prepack and inspect dist/types.d.mts. |
External Usage & Publishing
Consumption in Another Nuxt App
Install (after publishing to npm):
npm install @designmatters/uiAdd to nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@designmatters/ui'],
// optional future options
})Use components directly (auto-registered):
<template>
<DmButton variant="secondary">Click Me</DmButton>
</template>How Auto-Registration Works
module.ts calls addComponentsDir({ prefix: 'Dm', global: true }) so every SFC named DmXxx.vue in src/runtime/components becomes globally available as <DmXxx> without manual imports.
Accessing Design Tokens Externally
Tokens are provided via injection using the key designmatters:tokens:
const tokens = inject('designmatters:tokens')You can wrap this in a helper composable later for stronger typings.
Building & Publishing
Release script (semantic-ish flow):
npm run lint
npm run test
npm run prepack # builds dist (JS + types)
npm run release # runs above + changelogen + npm publish + git push --follow-tagsEnsure you are authenticated with npm login and have proper version bumps (managed by changelogen or manual package.json update before release).
Verifying Distribution
After npm run prepack:
| File | Expectation |
|------|-------------|
| dist/module.mjs | Exports Nuxt module with component registration. |
| dist/types.d.mts | Contains the component prop/emits types. |
Importing Specific Assets
Because components are globally registered at runtime, consumers rarely import from @designmatters/ui directly. If tree-shaking granular imports are needed later, expose them via explicit exports map entries.
Button Component (Completed Example)
DmButton illustrates the process:
- Implementation:
src/runtime/components/DmButton.vueconsuming injected design tokens. - Fixture:
test/fixtures/buttonwithpages/index.vue(primary) &pages/variants.vue(secondary, tertiary). - Tests:
test/dm-button.e2e.test.tscover default + variants. - Next enhancements: loading state visuals & a11y labelling patterns in Storybook.
Use this as a reference template for every future component.
Component Roadmap
The initial set of reusable building blocks we expect every Design Matters experience to share:
- Button family (primary, secondary, icon buttons with pixel-art frames)
- Typography primitives (headings, body text, pixelated display type)
- Form inputs (text, select, checkbox, radio, toggle) with accessible states
- Notifications & badges (toast, inline status chips, banners)
- Cards & panels (content containers, modular layouts)
- Navigation elements (top bar, sidebar, tab set, breadcrumb)
- Dialog & modal system (with focus trapping and themed chrome)
- Data display (tables, lists, metric tiles, progress indicators)
- Layout utilities (grid, spacing helpers, containers)
- Feedback & loaders (spinners, skeleton states, success/error states)
This list will evolve as we scope more internal projects—log updates in docs/component-inventory.md.
Contributing
We are currently preparing the foundation for the component library. Help by refining documentation, writing tests, or shaping the Storybook entries. Always:
- Sync documentation before submitting work.
- Run the test suite (
npm run test) and linting (npm run lint). - Capture context in
docs/progress-log.mdanddocs/design-decisions.mdfor every change.
