uxskill
Star on GitHub
Language
English 简体中文 繁體中文 日本語 한국어 हिन्दी Bahasa Indonesia Tiếng Việt ไทย العربية Español Français Deutsch Português Русский Türkçe Italiano

## The full axis table

Axis | 0.0 end | 1.0 end | Compiles to
--- | --- | --- | ---
`warmth` | cold | warm | RGB warmth-shift
`contrast` | flat | dramatic | Type scale ratio + weight curve
`density` | airy | dense | Spacing base + line-height
`geometry` | sharp | soft | Radius scale
`formality` | playful | corporate | Tracking + motion dampening
`motion` | still | kinetic | Animation timing + curve
`type_personality` | geometric | humanist | Display family bias

## Where the axis values come from

Each axis is derived through three steps:

### 1. Industry seed

17 documented industries each have a 7-axis seed dictionary. Unknown industries default to neutral 0.5 across the board.

    INDUSTRY_SEEDS = {
        "fintech-payments": {"warmth": 0.35, "contrast": 0.55, "density": 0.6,
                             "geometry": 0.4, "formality": 0.75, "motion": 0.45,
                             "type_personality": 0.4},
        "luxury":           {"warmth": 0.55, "contrast": 0.7, "density": 0.3,
                             "geometry": 0.55, "formality": 0.75, "motion": 0.4,
                             "type_personality": 0.75},
        "gaming":           {"warmth": 0.5,  "contrast": 0.85, "density": 0.7,
                             "geometry": 0.35, "formality": 0.3, "motion": 0.85,
                             "type_personality": 0.45},
        # ... 14 more
    }

### 2. Tone nudges

35+ documented tone tags push axes by ±0.10 to ±0.30:

    TONE_NUDGES = {
        "warm":         {"warmth": +0.20},
        "bold":         {"contrast": +0.25, "motion": +0.10},
        "minimal":      {"density": -0.30, "contrast": -0.10},
        "rounded":      {"geometry": +0.25, "warmth": +0.10},
        "corporate":    {"formality": +0.25, "warmth": -0.10},
        "kinetic":      {"motion": +0.25, "contrast": +0.10},
        "editorial":    {"type_personality": +0.25, "density": +0.10},
        # ... 28 more
    }

### 3. Forbidden clamps

The brief's `forbidden` list applies hard clamps to specific axes:

    FORBIDDEN_CLAMPS = {
        "playful":    ("formality", (0.6, 1.0)),
        "loud":       ("contrast",  (0.0, 0.5)),
        "dense":      ("density",   (0.0, 0.55)),
        "brutalism":  ("geometry",  (0.3, 1.0)),
    }

So `forbidden=["playful"]` clamps formality to at minimum 0.6, regardless of what the seed + nudges produced.

## How each axis lands as code

### warmth → palette RGB shift

The palette synthesizer mixes anchor colors from the chosen brand exemplars, then applies a warmth shift based on the axis value. Distance from 0.5 sets intensity, max ±18 RGB units.

    def _warmth_shift(rgb, warmth):
        delta = (warmth - 0.5) * 2     # -1.0 .. +1.0
        shift = 18 * delta             # ±18 max
        r, g, b = rgb
        return (r + shift, g + shift / 2, b - shift / 2)

So a brief with warmth=0.7 produces palette canvas ~12 R units warmer, ~6 G warmer, ~6 B cooler than the same brief at warmth=0.5.

### contrast → modular scale ratio + weight curve

Pick the type-size ratio from contrast:

- `contrast >= 0.7` → 1.333 (perfect fourth, loud)
- `contrast >= 0.4` → 1.250 (major third, balanced)
- `contrast < 0.4` → 1.200 (minor third, quiet)

So a "minimal/quiet" brief produces tightly-stepped headings (16/19.2/23/27.7/33.2). A "bold/dramatic" brief produces dramatic jumps (16/21.3/28.4/37.9/50.5).

### density → spacing base

This is where the axis interaction matrix kicks in (we documented density × formality conflict resolution in the launch post):

density | formality | base px
--- | --- | ---
> 0.65 | any | 4 (Bloomberg)
< 0.4 | > 0.7 | 12 (luxury)
< 0.4 | < 0.7 | 8 (airy SaaS)
else | any | 6 (mid)

### geometry → radius scale

Sharp + corporate = 2px (NYT). Soft + playful = 18px (Glossier). Otherwise blended target.

### formality → tracking + motion dampening

High formality tightens display letter-spacing (-0.025em vs -0.015em). Also dampens kinetic motion timings by 25% when motion is also high (the "Bloomberg with hover animations" case).

### motion → animation timing + curve pick

`base_ms = 220 - 80 * motion`, so a kinetic brief gets 140ms base timings, a still brief gets 220ms.

Curve picks:

- `motion > 0.7` → spring overshoot (`cubic-bezier(0.34, 1.56, 0.64, 1)`)
- `motion > 0.4` → ease-cinema (`cubic-bezier(0.22, 1, 0.36, 1)`)
- else → standard ease

### type_personality → display family bias

Low (geometric) biases the synthesizer toward Inter Tight / Söhne / Grotesque families. High (humanist) biases toward Bricolage Grotesque / IBM Plex / Source families. Body always anchors on Inter.

## What this means in practice

You ask for: `industry=fintech-payments, tone=["bold", "serious"]`.

1. Industry seed: warmth=0.35, contrast=0.55, density=0.6, geometry=0.4, formality=0.75, motion=0.45, type_personality=0.4
2. Apply "bold" nudge: contrast=0.55+0.25=0.80, motion=0.45+0.10=0.55
3. Apply "serious" nudge: formality=0.75+0.20=0.95
4. Final axes: (0.35, 0.80, 0.6, 0.4, 0.95, 0.55, 0.4)

The synthesizer compiles this to:

- Palette: cool-leaning (warmth 0.35 = cool shift) with high saturation accent (contrast 0.80)
- Type ratio: 1.333 (contrast 0.80 puts it in the "loud" branch)
- Spacing base: 4px (dense 0.6 + formal 0.95 → density wins, Bloomberg-style)
- Radius: small but not zero (geometry 0.4 + formal 0.95 → conservative)
- Tracking display: -0.025em (formality > 0.6 = tight)
- Motion timing: ~196ms base, +25% dampener (formality 0.95 dampens), final ~245ms
- Motion curve: ease-cinema (motion 0.55 is in the mid range)
- Display font: Inter Tight (type_personality 0.4 = geometric-leaning)

All deterministic. All from a brief with 3 fields. All offline, no LLM call.

## Try it yourself

    pip install uxskill
    uxskill synthesize \
      --industry fintech-payments \
      --tone bold --tone serious

    # Outputs: { mode: "pure_synthesis",
    #            axes: {warmth: 0.35, contrast: 0.80, ...},
    #            palette: {canvas: "#0a1014", ink: "#f6f8fa", ...},
    #            type_pair: {display: "Inter Tight", body: "Inter", ...},
    #            spacing: {base: 4, scale: [4, 8, 12, 20, 32, 52, 84]},
    #            radius: {base_px: 4, sm: 2, md: 4, lg: 6, xl: 10, pill: 999},
    #            motion: {base_ms: 245, fast_ms: 147, slow_ms: 441, ... } }

## Related

- Design tokens are the missing layer in AI coding
- The v3.0 launch · The Brain
- Brand specs are training data
- A deterministic design engine
- What closed-loop actually means