# DDS — the dani design system

DDS is the visual language shared by every dani product. It is deliberately narrow: one
typographic scale, one spacing grid, one set of components, one monochrome base. What varies
between products is exactly one token — the accent, which is the product's own brand colour.
Dot Red belongs to dani.go; it is not the system's accent.

Current version **1.1.0**, updated 8 April 2026. Tokens live at `shared/design/dot-tokens.yaml`
in the product monorepo; this project mirrors that contract.

> DDS is not a design system you customize. It's a design system you live inside.
> The constraints are the feature.

---

## Sources this project was built from

| Source | What it gave |
| --- | --- |
| `claivis/dani-dds` @ main, `lib/src` | The Flutter implementation. `skins/dds_skin.dart` is the canonical token source — colours, type, radii, spacing, sizing, component styles. Overrides the blog post wherever they disagree. |
| https://dani.zone/blog/dds | The DDS 1.1.0 specification as published — philosophy, colour, type, spacing, radii, borders, motion, components, usage rules, accessibility, platform implementations, governance. Primary source for everything in `tokens/`. |
| https://dani.zone/ | Homepage copy and structure for the marketing kit. |
| https://dani.zone/apps | The 12-app catalogue with taglines, categories and platform lists. |
| https://dani.zone/apps/go, /apps/ko | Product-page structure and copy. |
| https://dani.zone/pricing | Plans, FAQ copy. |
| `uploads/00-dds-foundation.html` | A dani.ko foundation sheet — the bilingual stack, the verification and quarantine patterns, and the navy accent deviation. |
| `uploads/logos-1785811417180.svg` | The dani app mark, 1024×1024. The only real brand asset supplied. |

Nothing here was reconstructed from screenshots.

---

## The company

**Claivis** ships the **dani** ecosystem: a set of small, single-purpose utility apps sold
individually or bundled as *dani.all* at $9.99/mo. Twelve apps ship today across finance, utility,
lifestyle, photo, travel, AI and productivity; the DDS colour table names eighteen. Made in Tokyo.

Two products are documented in depth here:

- **dani.go** — a travel concierge you plan a trip by talking to. Launching 2026.
- **dani.ko** — the access layer for Korea's consumer internet when you have no Korean phone
  number or residency card. Its distinguishing constraint is epistemic: every externally-sourced
  fact carries its source and the date it was last checked.

---

## Content fundamentals

**Voice.** Plain, declarative, unhedged. Sentences are short and often verbless fragments.
The system's own spec reads like a manual: "Memorize them. Never deviate." "No drop shadows.
Ever." Marketing copy is written the same way — "All your apps. One plan." "One thing, done well."

**Person.** Marketing addresses the reader as *you*, and the company as *we* only when explaining
a decision ("Why we built dani.go"). Product UI speaks as *we* when it is admitting a limit:
"We can't confirm this is still accurate right now." First-person plural is reserved for honesty,
not for salesmanship.

**Naming.** Products are always lowercase with the dot — `dani.ko`, `dani.subs`, `dani.all`.
Never "Dani.KO", never "DaniKo". The dot is part of the name.

**Casing.** Sentence case for headings and body. Uppercase is a typographic role, not emphasis:
it belongs to the mono label style (`MONTHLY TOTAL`, `RENEWS 04 APR · MONTHLY`, `VERIFIED`)
and to button labels. Never uppercase a sentence.

**Separators.** The middle dot `·` joins facts inside a label: `MONEY · VERIFIED 28 JUL`,
`iOS · Android · Web`. Em dashes carry an aside in body copy. Both are used heavily.

**Numbers and dates.** Dates in labels are `28.JUL.2026` or `04 APR`. Amounts keep their
currency symbol and full precision — `$214.32`, `₩1.24M`. All of it sets in Space Mono; the
mono face is how the reader knows a thing is data.

**Honesty rules.** dani.ko will not render an unknown as a positive: "Not known — check before
you go" is shipped copy. Offline states say what was saved and how old it is, never a spinner.
Facts past their re-check deadline are quarantined with copy that says the app cannot confirm
them. Feature copy names the limit alongside the promise ("Amex · patchy outside Seoul").

**Emoji.** None. Not in product, not in marketing, not in the spec. Symbols come from the type
families: `·`, `✓`, `×`, `−`, `+`.

---

## Visual foundations

**Colour.** A monochrome base per mode, plus one accent — the product's brand colour, applied
through a `[data-product]` scope. Twenty products, twenty accents, each a light/dark pair tuned
to its surfaces. Nothing else changes between products, which is what makes them read as one
family rather than one colour. As shipped, both ramps are Tailwind
slate: light runs #F9FAFB → #FFFFFF → #F3F4F6 with #E5E7EB hairlines and #111827 text; dark runs
#111827 → #1F2937 → #374151 with #F9FAFB text. The published spec describes something colder and
more absolute — matte #000000 dark, warm #F7F7F7 light — which the code does not do. See
"Code vs. published spec". The accent is Dot Red, shipped as a
pair (`#E80000` light, `#FF2D2D` dark) and never cross-used. Eighteen product hues exist for
app icons and marketing assets only.

**Type.** Two families, split by role. Space Grotesk is the UI voice; Space Mono is the data
voice and owns every number, date, code, label and timestamp. That split is the system's
signature. Nine steps, from Micro 8/700 to Display 28/600. Body is 15/500 — medium, never
regular. Labels are always uppercase, weight 700, 8–10px, tracked 0.08–0.12em.

**Spacing.** A 4px grid, described in the spec as non-negotiable. Screen padding 16, card padding
18, card-to-card gap 14, section gap 24.

**Radii.** Three: 2px for phone screens, 4px for every control and card, 8px for the phone frame.
No pills. No circles — except the small dot indicator, which the spec names as its one exception,
and which also forms the dot in the app mark. The app tile is the single exception to the
rectangular rule: a superellipse at r 0.42, exponent 5.

**Borders and depth.** Borders replace shadows entirely. 1px hairline for dividers and card
boundaries, 2px strong for emphasis and selected states. There are no drop shadows anywhere in
the system. Depth is expressed by stacking `surface` over `surface_2`, never by blur.

**Backgrounds.** Flat colour. No gradients, no patterns, no textures, no full-bleed imagery in
product. Marketing pages use product screenshots inside bordered cards, not as backdrops.
Transparency and blur are not part of the system.

**Motion.** Two durations — 100ms for instant feedback, 200ms for state transitions — always
`ease-out`, haptics light. No physics, no parallax, no particles, no flourishes. Reduced-motion
users get animation disabled, not shortened.

**Interaction states.** Hover and press are colour and border changes within the token set, at
100ms; nothing scales, lifts or glows. Selected state is the 2px accent border. Focus is a 2px
accent outline at 2px offset. Disabled drops to `surface_2` / `text_3` — §09 forbids accent on
disabled states.

**Cards.** Rectangle, 1px border, 4px radius, 18px padding, surface background, no shadow.
That is the whole definition; nearly every grouping in every product is this shape.

**Layout.** Bottom tab bar on mobile, sticky hairline header on web with a 1120px content column.
Grids are plain: 2, 3 or 4 equal columns with a 14px gap.

**Imagery.** Real product screenshots, presented inside bordered cards. There is no illustration
system, no iconography-as-decoration, and no photography treatment defined. Where imagery is
missing, this project renders a labelled slot rather than inventing a picture.

---

## Iconography

There is no icon set in the supplied sources. The spec's typographic argument — "a grid, a
monospace, weight, and whitespace do the work of a hundred icons" — is borne out by the product
screens, which carry almost no glyphs. What DDS uses instead:

- **Mono squares as avatars.** A 36px 4px-radius square holding one or two characters (`N`,
  `A6`, `05`) stands in for what other systems would make an icon or a circular avatar.
- **The 4px accent square** as bullet, active-tab indicator and section marker.
- **Unicode from the type families**: `·` `✓` `×` `−` `+`. Nothing else.
- **The app mark** is the only drawn artwork: a filled square, a white lowercase `d`, and the dot.

**No third-party icon library is referenced anywhere in the sources, and none has been
substituted here.** If a UI genuinely needs line icons, ask before adopting a set — it would be
a new decision for DDS, not a documented one.

Assets in `assets/icons/` are the supplied `dani.ko` mark plus recolours of that exact geometry
into the eighteen §02 product hues (light tuning, which is the one contrast-verified for white).
The recolours are derived, not supplied — see "Gaps and open questions".

---

## Code vs. published spec

`claivis/dani-dds` — a Flutter package — is the implementation, and `DdsSkin` in
`lib/src/skin/skins/dds_skin.dart` is what actually ships. It disagrees with the blog post on
**every neutral**. The code wins; the tokens here follow it.

| | Published on the blog | Shipped in `dds_skin.dart` |
| --- | --- | --- |
| light bg | #F7F7F7 | **#F9FAFB** |
| light surface_2 | #F0F0F0 | **#F3F4F6** |
| light border | #E0E0E0 | **#E5E7EB** |
| light text | #111111 | **#111827** |
| light text_2 | #666666 | **#6B7280** |
| light text_3 | #AAAAAA | **#9CA3AF** |
| dark bg | #000000 — "matte black, not charcoal" | **#111827** |
| dark surface | #0B0B0B | **#1F2937** |
| dark surface_2 | #141414 | **#374151** |
| dark border | #1F1F1F | **#374151** |
| dark text | #FFFFFF | **#F9FAFB** |

The shipped neutrals are Tailwind's slate ramp. Dot Red, the type families, the 4px radii, the
no-shadows rule and the 18/14/24 spacing all match the post exactly — the divergence is confined
to the greys, and it undoes the post's most-stated claim, that dark mode is matte black. The blog
values remain in `tokens/colors.css` as `--dds-spec-*` for reference.

**Four things still need a decision:**

1. **Which grey ramp is correct?** If the post is right, `dds_skin.dart` needs changing. If the
   code is right, §02 of the post is wrong and the "memorize them, never deviate" line is stale.
2. **The canonical YAML doesn't exist in this repo.** `dds_skin.dart` cites
   `shared/design/dds-tokens.yaml`; the post cites `shared/design/dot-tokens.yaml`. Point me at
   the real file and I'll mirror it exactly.
3. **The lockup has no definition in code.** `AppIcon`/`Wordmark` have no counterpart anywhere in
   the repo — no logo widget, no lockup ratios. The ratios here are measured off the shipped
   dani.go artwork; they should go into the package.
4. **The published spec's accent rule is wrong.** §02 states every product uses Dot Red for UI and
   that product colours "never appear in-app". In fact each product's brand colour *is* its UI
   accent. The post needs correcting, and `dds_skin.dart` hardcodes Dot Red as `primary` — it
   should read the product's colour instead.
5. **DDS is one skin of thirteen.** The repo also ships acid, atlas, brutalist, curator, gemini,
   midnight, neon, paper, scout, tokyo_pop and voyager. Should this design system cover only
   DDS, or the skin system as a whole?

## Changes to the published spec

Made at the system owner's direction, after the blog post was written. Anyone syncing
`dot-tokens.yaml` back to the monorepo should carry these across.

| Token | Published | Here |
| --- | --- | --- |
| `body` | 13/500 | **15/500** |
| `body_sm` | 12/500 | **13/500** |
| `brand.zone` | Electric Cyan #00F0FF / #008A94 | **Dark Grey #666666 / #333333** |
| accent model | one Dot Red for every product | **the product's own brand colour** |

The rest of the scale — Display, Title, Heading, Amount, Data, Label, Micro — is untouched, as
are the grid, radii, borders and motion.

### The lockup

The mark and the name are one object with fixed proportions. Callers pass **one** dimension —
the tile size **S** — and everything else derives:

| | |
| --- | --- |
| tile | **S** |
| tile shape | **superellipse**, `|x/r|⁵ + `|y/r|⁵ = 1`, r = **0.42 · S** — a continuous squircle, not a rounded rect. Measures as ~0.20 circular roundness, but reads far softer at small sizes. The mark and its dot are the system's only curves. |
| glyph placement | master glyph, unscaled, sitting **18/1024 above** the tile centre (top inset 0.286, bottom 0.321) |
| wordmark baseline | **optically centred on the tile** — no offset. The shipped artwork sits its text ~0.09 · S lower; that is deliberately not reproduced, because it misaligns against adjacent text on the same centre line. |
| gap, tile to name | **0.321 · S** |
| wordmark type size | **0.827 · S** |
| wordmark ink | **0.75 · S** tall — ascender to the *descender* of 'g', not to the baseline |
| ink top | **0.214 · S** below the tile's top edge |
| tracking | **−0.03em**, which closes the last 5% of the width |
| name | lowercase Space Grotesk 600, −0.03em, always with the dot |

Measured off the shipped dani.go lockup — tile 28px, gap 9px, ink 21px tall and 78px wide — and
applied everywhere the brand appears: site header,
footer, app-list rows, product-page headers. **Never set the wordmark's type size independently
of its tile** — a header whose name was sized to the nav and a card whose name was sized to the
body text are the same mark at two different ratios, which is how a brand stops looking like one.

**Named sizes.** Don't pick a number — pick a role:

| Scale | Tile | Use |
| --- | --- | --- |
| `compact` | 24px | dense rows, breadcrumbs, footnotes |
| `default` | 32px | list rows, footers, secondary placements |
| `primary` | 40px | **the main mark of a surface** — the dani.zone site header, a product-page header |
| `hero` | 64px | splash, marketing hero, store artwork |

Use `<Wordmark product="go" scale="primary" />`, not an `AppIcon` beside hand-set text. The
ratios and sizes also ship as CSS custom properties in `tokens/brand.css` and as
`brand_lockup` in `tokens/dot-tokens.yaml`, for surfaces that can't use the component.

Every tile carries the `d.` master glyph, at every size and for every product. Only the tile's
hue changes.

## dani.ko's third type family

dani.ko adds **IBM Plex Sans KR** for 한글, which DDS core does not cover and the product cannot
exist without. Its navy accent is no longer a deviation — it is simply its product colour, applied
the same way every other product's is.

## Gaps and open questions

1. **App icons are derived, not supplied.** Only the dani.ko mark was provided. The other
   eighteen in `assets/icons/` are that same file recoloured to the §02 light hues. If real
   per-product icon artwork exists, send it and these should be replaced.
2. **dani.do has no brand colour.** It ships on the site but is absent from the §02 table, so
   `AppIcon` falls back to navy for it. Which hue should it take?
3. **The site's real CSS was never read.** Marketing layouts were rebuilt from page text plus the
   DDS token values. Spacing follows the 4px grid but is not pixel-verified against production.
   A repo or a local folder would fix this.
4. **Fonts load from Google Fonts.** All three families are available there; no licensed files
   were supplied and none were substituted.
5. **dani.go feature screenshots are hot-linked** from `dani.zone/screenshots/go/` and do not
   load cross-origin. Local copies in `assets/screenshots/` would make the kits self-contained.
6. **Two homepage bundle stats are unknown.** The live counters animate on scroll and extract as
   `0`, so "SEPARATE VALUE" and "YOU SAVE" have no sourced figure and render as `—`. Send the
   real numbers and they go straight in.
7. **No dark-mode marketing site.** The spec ships both modes; the site kit is light only,
   because nothing in the sources shows the dark web treatment.

---

## Index

**Root**
- `styles.css` — the entry point. Imports only; link this one file.
- `readme.md` — this document.
- `SKILL.md` — Agent Skills manifest, for using this system in Claude Code.

**`tokens/`**
- `colors.css`, `brand-colors.css`, `typography.css`, `spacing.css`, `radii.css`,
  `borders.css`, `motion.css`, `fonts.css` — CSS custom properties.
- `themes/products.css` — one `[data-product]` accent scope per product.
- `dot-tokens.yaml` — the stated source of truth, mirrored.
- `dds_tokens.dart`, `DdsTokens.swift` — hand-mirrored platform token files.

**`components/`**
- `core/` — Button, Chip, Card, SectionHeader, Input, Gauge, FocusRing
- `data/` — ListItem, DataCard
- `navigation/` — TabBar, PhoneFrame
- `brand/` — AppIcon, Wordmark
- `product/` — BilingualStack, VerificationFooter *(dani.ko)*

**`ui_kits/`**
- `dani_ko_app/` — mobile kit: trip, place, money, offline
- `dani_zone_web/` — marketing site: home, apps, pricing, product-page template
- `dani_go_web/`, `dani_ko_web/` — product pages built on that template

**`guidelines/`** — 19 specimen cards covering colour, type, spacing, radii, borders, motion,
accessibility and brand.

**`assets/icons/`** — 19 app marks as SVG.

### Intentional additions

DDS §08 defines six component families. Four more were added because the spec references them
elsewhere but does not draw them: **SectionHeader** (§03's label-above-header pattern),
**TabBar** (§03 names the bottom tab bar), **FocusRing** (§10's focus treatment), and
**PhoneFrame** (§05 reserves the 8px radius for it). **AppIcon** and **Wordmark** exist because
the mark was supplied and needed a component. **BilingualStack** and **VerificationFooter** come
from the dani.ko foundation sheet, not from DDS core, and are grouped separately for that reason.
