@kiva/kv-tokens 4.0.3 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/make-kit/guidelines/Guidelines.md +13 -0
- package/dist/make-kit/guidelines/foundations/color-themes.md +317 -0
- package/dist/make-kit/guidelines/foundations/color.md +235 -0
- package/dist/make-kit/guidelines/foundations/layout.md +194 -0
- package/dist/make-kit/guidelines/foundations/radius.md +176 -0
- package/dist/make-kit/guidelines/foundations/spacing.md +210 -0
- package/dist/make-kit/guidelines/foundations/tailwind.md +226 -0
- package/dist/make-kit/guidelines/foundations/typography.md +257 -0
- package/dist/make-kit/guidelines/setup.md +34 -0
- package/dist/make-kit/styles.css +4912 -0
- package/package.json +8 -4
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# Kiva Layout
|
|
2
|
+
|
|
3
|
+
**When to use:** When designing or implementing any page layout, responsive composition, or component that places content into columns — choosing a breakpoint, deciding a column span, reasoning about gutter vs. margin, nesting a grid inside a component, or handing off a multi-tier design to development. Reference design intent first; verify breakpoint and gap values against current code before relying on them.
|
|
4
|
+
|
|
5
|
+
## Source of truth
|
|
6
|
+
|
|
7
|
+
This skill captures the **layout grid system** as defined in Figma (the Kiva Ecosystem 2026 file). Figma is the canonical source for design intent: which tiers exist, what their column/gutter/margin values are, and how content is meant to sit on the grid.
|
|
8
|
+
|
|
9
|
+
Numeric values, tier names, and any class references in this document reflect the Figma specifications. The shipped code in `@kiva/kv-tokens` and `@kiva/kv-components` may temporarily lag behind these specs while the layout token sync work is in progress. **Verify any breakpoint value, gap class, or grid utility against the current code before depending on it.** See "Outstanding discrepancies" at the end of this skill for known gaps.
|
|
10
|
+
|
|
11
|
+
## Why a grid
|
|
12
|
+
|
|
13
|
+
Kiva uses layout grids so that content aligns consistently across all screen sizes. The grid provides a shared structure that makes layout decisions predictable and repeatable — designers and developers reach the same answer without re-deriving it per screen.
|
|
14
|
+
|
|
15
|
+
Common alternative names you may see used interchangeably: **columns**, **rows**, **guides**, **layout guides**, **grid system**.
|
|
16
|
+
|
|
17
|
+
## Anatomy
|
|
18
|
+
|
|
19
|
+
A page grid has three parts.
|
|
20
|
+
|
|
21
|
+
| Part | What it is |
|
|
22
|
+
|---|---|
|
|
23
|
+
| **Column** | The vertical divisions that content aligns to. Column count and width change based on the size of the screen or container. |
|
|
24
|
+
| **Gutter** | The empty space between columns, separating elements and preventing content from merging. Gutter stays the same size as the container resizes within a tier. |
|
|
25
|
+
| **Margin** | The space between the outermost columns and the container edge. This value stays constant across the full range of each tier. |
|
|
26
|
+
|
|
27
|
+
The mental model: margins frame the page; columns hold content; gutters keep content blocks from touching.
|
|
28
|
+
|
|
29
|
+
## Breakpoint tiers — page grid specs
|
|
30
|
+
|
|
31
|
+
The canonical reference for all five breakpoint tiers:
|
|
32
|
+
|
|
33
|
+
| Tier | Range | Columns | Gutter | Margin |
|
|
34
|
+
|---|---|---|---|---|
|
|
35
|
+
| **XS** | 0 – 319px | 4 | 16px | 20px |
|
|
36
|
+
| **SM** | 320px – 733px | 4 | 16px | 20px |
|
|
37
|
+
| **MD** | 734px – 1023px | 8 | 24px | 32px |
|
|
38
|
+
| **LG** | 1024px – 1439px | 12 | 32px | 64px |
|
|
39
|
+
| **XL** | 1440px – up | 12 | 32px | 120px |
|
|
40
|
+
|
|
41
|
+
> **Figma label note (SM columns):** the SM *breakpoint diagram* in Figma is tagged `col: 8`, but that label is stale — the spec table above, the diagram's own drawn columns, and the "Building Layouts" SM example all show **4** columns for SM. Use **4**; the `col: 8` tag is a Figma typo flagged to the design system team.
|
|
42
|
+
|
|
43
|
+
### What XS is for
|
|
44
|
+
|
|
45
|
+
XS exists as a **boundary tier only**. There is no XS Figma frame to design in — SM at 390px is the practical mobile design frame. XS guarantees the grid still resolves below 320px if a viewport ever lands there.
|
|
46
|
+
|
|
47
|
+
### Content max width
|
|
48
|
+
|
|
49
|
+
Even in LG and XL, content is constrained within a **1200px container**. Actual grid content caps at **~1152px** after accounting for padding and margins. Wider viewports show additional negative space at the edges; the grid does not keep growing.
|
|
50
|
+
|
|
51
|
+
### Relationship to the spacing scale
|
|
52
|
+
|
|
53
|
+
Every gutter and margin value above is a multiple of 4 — they sit on the same **4px-grained ramp** that powers the spacing system (see the [spacing](spacing.md) skill). Gutters map cleanly to spacing tokens (16 = `2`, 24 = `3`, 32 = `4`); the larger margin values (64 = `8`, 120 = `15`) also resolve on the ramp. Designers and developers can reason about both systems against one shared scale.
|
|
54
|
+
|
|
55
|
+
## Nested grids
|
|
56
|
+
|
|
57
|
+
A nested grid is a grid placed **inside a column of an existing page grid**. Use nested grids to organize the internal layout of a component (e.g., stats inside a loan card) without affecting the overall page structure.
|
|
58
|
+
|
|
59
|
+
| Tier | Columns | Gutter | Margin |
|
|
60
|
+
|---|---|---|---|
|
|
61
|
+
| **XS** | 4 | 16px | — |
|
|
62
|
+
| **SM** | 4 | 16px | — |
|
|
63
|
+
| **MD** | 8 | 24px | — |
|
|
64
|
+
| **LG** | 12 | 32px | — |
|
|
65
|
+
| **XL** | 12 | 32px | — |
|
|
66
|
+
|
|
67
|
+
**There is no margin on a nested grid** — that is the only structural difference from the page grid. Columns and gutters match the parent tier.
|
|
68
|
+
|
|
69
|
+
### When to nest vs. span the parent
|
|
70
|
+
|
|
71
|
+
The decision comes down to whether a section needs to align with the rest of the page or only with itself.
|
|
72
|
+
|
|
73
|
+
| Use case | Approach | Example | Gutter |
|
|
74
|
+
|---|---|---|---|
|
|
75
|
+
| Main page sections | Span the parent grid | Nav, hero, content rows | Parent tier gutter |
|
|
76
|
+
| Internal component layout | Use a nested grid | Stats inside a loan card | Parent tier gutter |
|
|
77
|
+
| Spacing between items | Always use the tier gutter | Gap between cards, columns | Parent tier gutter |
|
|
78
|
+
|
|
79
|
+
## Aligning content on the grid
|
|
80
|
+
|
|
81
|
+
### Place content at the column edge, not the gutter
|
|
82
|
+
|
|
83
|
+
Content starts at the **column edge**. Gutters are breathing room between content blocks — they are not a starting point for content.
|
|
84
|
+
|
|
85
|
+
### Span
|
|
86
|
+
|
|
87
|
+
Content can span one or more columns. Use `span` to control how wide a content block is within the grid.
|
|
88
|
+
|
|
89
|
+
- If a span value **exceeds** the available columns, the content shrinks to fill what's available.
|
|
90
|
+
- If there is not enough room left in a row, the content **wraps to the next row**.
|
|
91
|
+
|
|
92
|
+
### Intrinsic-width content
|
|
93
|
+
|
|
94
|
+
Not all content needs to span the full column width. Tags, badges, and pill buttons have a fixed natural size and should remain at their default width.
|
|
95
|
+
|
|
96
|
+
- **Do** keep the intrinsic-width content at its natural size.
|
|
97
|
+
- **Don't** stretch or shrink an intrinsic-width component to fill the grid.
|
|
98
|
+
|
|
99
|
+
### Fixed-width content
|
|
100
|
+
|
|
101
|
+
Some elements — most often a side navigation panel — have a fixed width that does not align with the column grid. When placed next to other content:
|
|
102
|
+
|
|
103
|
+
- The gap between the fixed-width element and the adjacent content uses the **parent grid's gutter value**.
|
|
104
|
+
- The remaining content continues to align with the outer grid.
|
|
105
|
+
|
|
106
|
+
- **Do** follow the parent screen's layout gutter for the gap.
|
|
107
|
+
- **Don't** abut adjacent content directly to the fixed-width edge with no gutter, and don't push the adjacent content past the next column edge.
|
|
108
|
+
|
|
109
|
+
## Building layouts across tiers
|
|
110
|
+
|
|
111
|
+
The same composition is expressed differently per tier because column counts change. Use spans that make sense for the tier:
|
|
112
|
+
|
|
113
|
+
- **LG (12 columns):** Full-width sections use `span 12`. A three-up row of cards uses three `span 4` items.
|
|
114
|
+
- **MD (8 columns):** Full-width sections use `span 8`. A two-up row uses two `span 4` items.
|
|
115
|
+
- **SM (4 columns):** Almost everything stacks into single `span 4` blocks.
|
|
116
|
+
|
|
117
|
+
When a design needs to shift from a multi-up row to a stack on a smaller tier, redo the span — don't try to preserve the same span value across tiers with different column counts.
|
|
118
|
+
|
|
119
|
+
## Implementation in Figma
|
|
120
|
+
|
|
121
|
+
### The grid is a guide, not a cage
|
|
122
|
+
|
|
123
|
+
Use the grid as a guide, not a constraint. It exists to create alignment and rhythm, but:
|
|
124
|
+
|
|
125
|
+
- Full-bleed elements (e.g., a hero background, a section banner) may extend beyond the grid.
|
|
126
|
+
- Intrinsic elements like tags, badges, and buttons do not need to align to column edges.
|
|
127
|
+
|
|
128
|
+
### Switching tiers mid-design
|
|
129
|
+
|
|
130
|
+
Set your Figma frame to the **official width for the tier you're designing for**. Designing at an arbitrary width and approximating the grid causes inconsistencies during handoff. The tier ranges are absolute: anything 1024–1439px should use LG; anything 734–1023px should use MD; etc.
|
|
131
|
+
|
|
132
|
+
### Handoff clarity
|
|
133
|
+
|
|
134
|
+
When sharing specs with developers, **always note which tier the design is built at** and **whether any nested grids are in use**. Developers working mobile-first need to know which breakpoint token a component's internal layout maps to.
|
|
135
|
+
|
|
136
|
+
### Applying layout grids in Figma
|
|
137
|
+
|
|
138
|
+
Layout grid styles are published in the Kiva Ecosystem library. Apply the matching style to a frame:
|
|
139
|
+
|
|
140
|
+
- `XS 0 - 319` — page grid for XS
|
|
141
|
+
- `SM 320 - 733` — page grid for SM
|
|
142
|
+
- `MD 734 - 1023` — page grid for MD
|
|
143
|
+
- `LG 1024 - 1439` — page grid for LG
|
|
144
|
+
- `XL 1440 - up` — page grid for XL
|
|
145
|
+
- `XS-Nested 0 - 319`, `SM-Nested 320 - 733`, … — nested grid variants
|
|
146
|
+
|
|
147
|
+
Match the style to the frame width and to whether the frame represents a page or a nested context.
|
|
148
|
+
|
|
149
|
+
## Usage rules
|
|
150
|
+
|
|
151
|
+
### Do
|
|
152
|
+
|
|
153
|
+
- Pick the tier by viewport width using the absolute ranges above.
|
|
154
|
+
- Align content to **column edges**, not gutters.
|
|
155
|
+
- Use the **tier's** gutter for spacing between grid items (don't substitute an arbitrary gap).
|
|
156
|
+
- Use a **nested grid** for component-internal layout that shouldn't be coupled to the page grid.
|
|
157
|
+
- Note tier and nested-grid usage when handing a design to developers.
|
|
158
|
+
|
|
159
|
+
### Don't
|
|
160
|
+
|
|
161
|
+
- Don't approximate the grid by designing at non-tier widths.
|
|
162
|
+
- Don't start content in the gutter or treat the gutter as a content well.
|
|
163
|
+
- Don't stretch intrinsic-width components (tags, badges, pills) to fill columns.
|
|
164
|
+
- Don't add a margin to a nested grid — nested grids have no margin by design.
|
|
165
|
+
- Don't reuse the same span number across tiers without re-checking against the tier's column count.
|
|
166
|
+
|
|
167
|
+
## Using with Tailwind
|
|
168
|
+
|
|
169
|
+
The layout primitives come from the `@kiva/kv-tokens` Tailwind preset. Haven't registered it yet? See [tailwind → Consuming the preset](tailwind.md#consuming-the-preset). Not using the preset? See [Without the preset](#without-the-preset) below.
|
|
170
|
+
|
|
171
|
+
Breakpoints are **mobile-first min-width screens: `md`, `lg`, `xl`** (plus a `print` screen) — there is **no `sm` and no `2xl`**. Unprefixed utilities are the base tier, so the design system's XS and SM tiers both fall under "no prefix" in Tailwind; layer `md:` / `lg:` / `xl:` on top. See [tailwind → Breakpoints are `md` / `lg` / `xl`](tailwind.md#breakpoints-are-md--lg--xl-mobile-first-no-sm). Build grids with `tw-grid` and the column/gap utilities, or use `KvGrid` (with the gutter caveat below).
|
|
172
|
+
|
|
173
|
+
**Shipped breakpoint values** (verify against `@kiva/kv-tokens/configs/tailwind.config.js` / `tokens/core/size.json`): `md: 734`, `lg: 1024`, `xl: 1440`.
|
|
174
|
+
|
|
175
|
+
### Without the preset
|
|
176
|
+
|
|
177
|
+
- **Kiva (or Kiva-adjacent) repo, preset not registered yet:** install and register it — [tailwind → Consuming the preset](tailwind.md#consuming-the-preset).
|
|
178
|
+
- **Stock-Tailwind / non-Kiva project:** define `md` / `lg` / `xl` screens at the values above in your own config (or use arbitrary min-width media), and set gutters/margins explicitly from the [grid specs](#breakpoint-tiers--page-grid-specs). Copied values are point-in-time.
|
|
179
|
+
|
|
180
|
+
## Outstanding discrepancies
|
|
181
|
+
|
|
182
|
+
- **XS and SM tiers are not named breakpoints in code** — mobile is the unprefixed default; the XS/SM values in this skill are design-side targets only.
|
|
183
|
+
- **`KvGrid`** ([`@kiva/kv-components/src/vue/KvGrid.vue`](../../../kv-components/src/vue/KvGrid.vue)) hard-codes its gap as `tw-gap-2 md:tw-gap-3 lg:tw-gap-3.5`, which **does not match** the Figma tier gutters (16 / 16 / 24 / 32 / 32). For spec-accurate gutters, set them explicitly with gap utilities rather than relying on `KvGrid`'s defaults.
|
|
184
|
+
- **Margins** (20 / 20 / 32 / 64 / 120) and the **1200px content max-width** are not shipped as named tokens; page-container behavior lives per-consumer today.
|
|
185
|
+
|
|
186
|
+
When you find a divergence between this skill and the shipped tokens/components, flag it — each is a data point for the design-system team.
|
|
187
|
+
|
|
188
|
+
## Figma source references
|
|
189
|
+
|
|
190
|
+
- Layout Overview: node `17968:7844`
|
|
191
|
+
- Layout Grid: node `17968:7170`
|
|
192
|
+
- Layout Guidelines: node `17968:7175`
|
|
193
|
+
|
|
194
|
+
File: `TPmBUB4olYPMF6glEhBGDG` (Ecosystem 2026 — WIP)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Kiva Radius
|
|
2
|
+
|
|
3
|
+
**When to use:** When designing or implementing any UI that has corners — buttons, cards, modals, inputs, chips, tags, avatars, images, section frames. Use it whenever someone reaches for a raw px corner-radius value, when nesting one rounded element inside another, or when picking a Tailwind `tw-rounded-*` utility. Reference design intent first; verify token names against current code before relying on them.
|
|
4
|
+
|
|
5
|
+
## Source of truth
|
|
6
|
+
|
|
7
|
+
This skill captures the **border-radius system** as defined in Figma (the Kiva Ecosystem 2026 file). Figma is the canonical source for design intent: which tokens exist, what each represents, and how to choose between them for any given component.
|
|
8
|
+
|
|
9
|
+
Numeric values, token names, and class references in this document reflect the Figma specifications. The shipped code in `@kiva/kv-tokens` may temporarily lag behind these specs while the token sync work is in progress. **Verify any token reference against the current code before depending on it.** See "Outstanding discrepancies" at the end of this skill for known gaps.
|
|
10
|
+
|
|
11
|
+
## Why radius matters
|
|
12
|
+
|
|
13
|
+
Radius shapes the corners of UI elements — and corner shape communicates **warmth, hierarchy, and interactivity**. Kiva's system defines 8 tokens running from square (`none`) to fully rounded (`full`), each mapped to a specific component family.
|
|
14
|
+
|
|
15
|
+
Using defined tokens instead of arbitrary values gets us three things:
|
|
16
|
+
|
|
17
|
+
1. Visual consistency across screens and components.
|
|
18
|
+
2. **Concentric corners** — proportional relationships between nested elements so corner curves look intentional.
|
|
19
|
+
3. Cleaner design-to-engineering handoff with no surprise pixel values to translate.
|
|
20
|
+
|
|
21
|
+
Common alternative names you may see used interchangeably: **border radius**, **rounding**, **corner radius**.
|
|
22
|
+
|
|
23
|
+
## Design principles
|
|
24
|
+
|
|
25
|
+
1. **Contextual hierarchy.** Radius signals element importance. Larger tokens (`base`, `lg`, `xl`) mark prominent interactive surfaces — buttons, cards, section frames. Smaller tokens (`xs`, `sm`) serve utility-first components like inputs and chips.
|
|
26
|
+
2. **Concentric consistency.** When elements are nested, the inner radius equals the outer radius minus the gap (see the [inner-radius formula](#inner-radius-the-concentric-formula)). A flat inner corner inside a rounded outer corner reads as unintentional.
|
|
27
|
+
3. **Scale discipline.** Always reach for a token — in design and in code. Never introduce arbitrary radius values. This keeps the system predictable, token-auditable, and clean for engineering handoff.
|
|
28
|
+
|
|
29
|
+
## The scale
|
|
30
|
+
|
|
31
|
+
Each token pairs a value with a component family. Pick by what the component *is*, not by eyeballing a curve.
|
|
32
|
+
|
|
33
|
+
| Token | Value | Tailwind class | Use cases |
|
|
34
|
+
|---|---|---|---|
|
|
35
|
+
| **none** | 0px | `tw-rounded-none` | Table cells; marketing photos as needed |
|
|
36
|
+
| **xs** | 4px | `tw-rounded-xs` | Form inputs, dropdowns, text areas, combo boxes |
|
|
37
|
+
| **sm** | 8px | `tw-rounded-sm` | Chips, table frame, large rectangular achievement badges |
|
|
38
|
+
| **md** | 12px | `tw-rounded-md` | Tooltips, stats tiles, selection tiles, sticky banners/footers, images, photos |
|
|
39
|
+
| **base** | 16px | `tw-rounded` | Buttons, modal, lightbox, toast, bottom sheet, mini cards |
|
|
40
|
+
| **lg** | 20px | `tw-rounded-lg` | All standard card outer corners, loan cards, slider cards, desktop images (except branding/marketing) |
|
|
41
|
+
| **xl** | 24px | `tw-rounded-xl` | Section frame |
|
|
42
|
+
| **full** | 9999px | `tw-rounded-full` | Icon buttons, tags / small badges, avatars, toggles |
|
|
43
|
+
|
|
44
|
+
### Anatomy of a token
|
|
45
|
+
|
|
46
|
+
Every radius token is defined by three properties:
|
|
47
|
+
|
|
48
|
+
- **Token name** — the identifier used in Figma and code. The 16px token is named **`base`** in Figma (renamed for visual consistency with the `xs / sm / md / lg / xl` scale; Figma treats the older **`default`** as the legacy name). In code it is still **`default`**, because Tailwind's `DEFAULT` key convention is what generates the unsuffixed `tw-rounded` utility. The two names refer to the same token — each is the one to type in its own surface.
|
|
49
|
+
- **Pixel value** — the corner radius. The scale runs `0, 4, 8, 12, 16, 20, 24, 9999`.
|
|
50
|
+
- **Tailwind class** — the utility generated from the token. Because the 16px token maps to the **`DEFAULT`** key in Tailwind, the utility is just `tw-rounded` (no suffix).
|
|
51
|
+
|
|
52
|
+
### The `tw-rounded` gotcha
|
|
53
|
+
|
|
54
|
+
In stock Tailwind, `rounded` (no suffix) is a small radius — pill-shaped corners come from `rounded-full`. In **Kiva's Tailwind preset** that's not true: `tw-rounded` resolves to **16px** (the `base` token), not to a pill. Use **`tw-rounded-full`** when you want a pill or circle.
|
|
55
|
+
|
|
56
|
+
If you're porting code from a standard Tailwind project, audit every bare `rounded` — what was a subtle curve there is a noticeably rounded 16px corner here.
|
|
57
|
+
|
|
58
|
+
## Inner radius — the concentric formula
|
|
59
|
+
|
|
60
|
+
When a component is nested flush inside a container, calculate its corner radius:
|
|
61
|
+
|
|
62
|
+
> **inner radius = outer radius − gap**
|
|
63
|
+
|
|
64
|
+
This prevents *visual inversion* — the failure mode where inner corners look larger than outer corners, which breaks the spatial hierarchy.
|
|
65
|
+
|
|
66
|
+
Worked examples (outer / gap → content):
|
|
67
|
+
|
|
68
|
+
| Outer | Gap | Content (inner) |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| 16px | 8px | 8px |
|
|
71
|
+
| 20px | 8px | 12px |
|
|
72
|
+
| 20px | 16px | 4px |
|
|
73
|
+
| 24px | 16px | 8px |
|
|
74
|
+
|
|
75
|
+
### When math suggests something awkward
|
|
76
|
+
|
|
77
|
+
For some cases, lean into an **8px radius** for a more effortless look, even if the formula suggests 4px. Treat the formula as a baseline, not a tyrant — the goal is optical consistency, not strict arithmetic.
|
|
78
|
+
|
|
79
|
+
### Independent content — when not to use the formula
|
|
80
|
+
|
|
81
|
+
When inner elements are **clearly independent** of the container (e.g., mini cards laid out inside a wide banner, where the cards have their own visual identity), the inner element should use its **own component token radius** rather than the container's formula.
|
|
82
|
+
|
|
83
|
+
The one constraint that still holds: **inner radius must always be ≤ outer radius**. Even with independent content, never let a child have a more rounded corner than its parent — that's visual inversion in the other direction.
|
|
84
|
+
|
|
85
|
+
## Best practices
|
|
86
|
+
|
|
87
|
+
### Use the formula inside a card
|
|
88
|
+
|
|
89
|
+
- **Do** compute the inner radius from the outer + gap when content sits flush inside a rounded container.
|
|
90
|
+
- **Don't** use a flat `0px` corner radius inside a rounded container. Always calculate the inner radius.
|
|
91
|
+
|
|
92
|
+
### Keep corners symmetric unless there's a structural reason
|
|
93
|
+
|
|
94
|
+
- **Do** apply the same radius to all four corners of a component.
|
|
95
|
+
- **Don't** apply different radius values to different corners arbitrarily. Asymmetric rounding reads as inconsistency unless it has a structural justification — for example, a card visually attached to the bottom of a tab bar where the top corners are flat to indicate continuity.
|
|
96
|
+
|
|
97
|
+
## Component → token quick reference
|
|
98
|
+
|
|
99
|
+
For fast lookup at design or build time, here's the inverse of the table above — common component types and the token they map to:
|
|
100
|
+
|
|
101
|
+
| Component | Token | Class |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| Dropdown, text input, text area, combo box | `xs` | `tw-rounded-xs` |
|
|
104
|
+
| Chip, table frame, achievement badge | `sm` | `tw-rounded-sm` |
|
|
105
|
+
| Tooltip, mobile image, stats tile, selection tile, sticky banner/footer | `md` | `tw-rounded-md` |
|
|
106
|
+
| Button, toast, lightbox, modal, bottom sheet, mini card | `base` | `tw-rounded` |
|
|
107
|
+
| Card (standard), loan card, slider card, desktop image | `lg` | `tw-rounded-lg` |
|
|
108
|
+
| Section frame | `xl` | `tw-rounded-xl` |
|
|
109
|
+
| Icon button, tag, small badge, avatar, toggle | `full` | `tw-rounded-full` |
|
|
110
|
+
| Table cell, raw marketing photo | `none` | `tw-rounded-none` |
|
|
111
|
+
|
|
112
|
+
## Usage rules
|
|
113
|
+
|
|
114
|
+
### Do
|
|
115
|
+
|
|
116
|
+
- Pick a token by **component type** using the scale table above.
|
|
117
|
+
- Use the inner-radius formula whenever content is nested flush inside a rounded container.
|
|
118
|
+
- Apply the same radius to all four corners by default.
|
|
119
|
+
- Use `tw-rounded-full` (not bare `tw-rounded`) when you want a pill or circle.
|
|
120
|
+
|
|
121
|
+
### Don't
|
|
122
|
+
|
|
123
|
+
- Don't write arbitrary radius values (`border-radius: 10px`, `tw-rounded-[10px]`). If no token fits, that's a design-system conversation, not a one-off.
|
|
124
|
+
- Don't leave a flat (`0px`) corner against a rounded container.
|
|
125
|
+
- Don't let an inner radius exceed its container's radius — visual inversion in either direction breaks the spatial hierarchy.
|
|
126
|
+
- Don't asymmetrically round corners without a structural reason.
|
|
127
|
+
|
|
128
|
+
### Need something not covered here?
|
|
129
|
+
|
|
130
|
+
Open a request with the design system team. Include the use case, the component, and why the existing tokens don't fit.
|
|
131
|
+
|
|
132
|
+
## How to use in Figma
|
|
133
|
+
|
|
134
|
+
All radius tokens are published as variables in the Kiva Ecosystem library. When applying a radius:
|
|
135
|
+
|
|
136
|
+
- Bind the corner-radius property to the radius variable instead of typing a number.
|
|
137
|
+
- Hover a variable to see its value and intended use cases.
|
|
138
|
+
- Before handoff, inspect the frame: if the corner-radius field shows a raw number instead of a variable name, **re-bind it from the library**. A raw value is a detached decision that won't follow updates and leaves developers without a token to map to.
|
|
139
|
+
|
|
140
|
+
## Using with Tailwind
|
|
141
|
+
|
|
142
|
+
The radius utilities below come from the `@kiva/kv-tokens` Tailwind preset. Haven't registered the preset yet? See [tailwind → Consuming the preset](tailwind.md#consuming-the-preset). Not using the preset at all? See [Without the preset](#without-the-preset) below.
|
|
143
|
+
|
|
144
|
+
Pick the class straight from the [scale table](#the-scale) above — each token's `tw-rounded-*` utility is listed there. The one trap carried over from stock Tailwind: **`tw-rounded` (no suffix) is 16px here**, not a small radius; use `tw-rounded-full` for a pill or circle. See [The `tw-rounded` gotcha](#the-tw-rounded-gotcha) above, and [tailwind](tailwind.md#border-radius-is-token-driven-and-tw-rounded--pill) for why.
|
|
145
|
+
|
|
146
|
+
**Shipped today** (verify against `@kiva/kv-tokens/configs/tailwind.config.js → theme.borderRadius` before depending):
|
|
147
|
+
|
|
148
|
+
- `tw-rounded-none` → `0px` (hardcoded in the preset)
|
|
149
|
+
- `tw-rounded-xs` → `4px` (from `radii.xs`)
|
|
150
|
+
- `tw-rounded-sm` → `8px` (from `radii.sm`)
|
|
151
|
+
- `tw-rounded-md` → `12px` (from `radii.md`)
|
|
152
|
+
- `tw-rounded` → `16px` (the `DEFAULT` key, from `radii.default`)
|
|
153
|
+
- `tw-rounded-lg` → `20px` (from `radii.lg`)
|
|
154
|
+
- `tw-rounded-xl` → `24px` (from `radii.xl`)
|
|
155
|
+
- `tw-rounded-full` → `500rem` (hardcoded in the preset; functionally a pill at any real width)
|
|
156
|
+
|
|
157
|
+
The 16px token is **`default` in code** and **`base` in Figma** — Tailwind's `DEFAULT` key generates the unsuffixed `tw-rounded` utility, so the mapping resolves through `DEFAULT` regardless of the source name. In code use `default` / `DEFAULT`; in Figma use `base`.
|
|
158
|
+
|
|
159
|
+
### Without the preset
|
|
160
|
+
|
|
161
|
+
- **Kiva (or Kiva-adjacent) repo, preset not registered yet:** install and register it — [tailwind → Consuming the preset](tailwind.md#consuming-the-preset). Until then, arbitrary values (`rounded-[16px]`) are a stopgap.
|
|
162
|
+
- **Stock-Tailwind / non-Kiva project:** use arbitrary values from the [scale table](#the-scale) (`rounded-[16px]`, `rounded-[20px]`). These are point-in-time copies of the token values, so re-check them if the scale changes.
|
|
163
|
+
|
|
164
|
+
## Outstanding discrepancies
|
|
165
|
+
|
|
166
|
+
- **`none` (0px) and `full` (500rem) are hardcoded in the preset** rather than flowing from `tokens/core/size.json` like the rest of the scale. A sync gap worth closing.
|
|
167
|
+
|
|
168
|
+
When you find a divergence from the shipped tokens, flag it — each is a data point for the design-system team.
|
|
169
|
+
|
|
170
|
+
## Figma source references
|
|
171
|
+
|
|
172
|
+
- Radius Overview: node `18929:1295`
|
|
173
|
+
- Radius Scale: node `18931:1295`
|
|
174
|
+
- Radius Guidelines: node `18932:1295`
|
|
175
|
+
|
|
176
|
+
File: `TPmBUB4olYPMF6glEhBGDG` (Ecosystem 2026 — WIP)
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Kiva Spacing
|
|
2
|
+
|
|
3
|
+
**When to use:** When designing or implementing any UI that involves space — section gaps, component padding, gaps inside a component, padding inside a card or modal, or any auto-layout gap/padding decision in Figma. Use it whenever someone reaches for a raw pixel value, or when picking between two tokens that happen to share the same number. Reference design intent first; verify token names against current code before relying on them.
|
|
4
|
+
|
|
5
|
+
## Source of truth
|
|
6
|
+
|
|
7
|
+
This skill captures the **semantic spacing system** as defined in Figma (the Kiva Ecosystem 2026 file). Figma is the canonical source for design intent: which categories exist, what each token means, when to use it, and how spacing values are expected to scale across breakpoints.
|
|
8
|
+
|
|
9
|
+
Numeric values and token names in this document reflect the Figma specifications. The shipped code in `@kiva/kv-tokens` may temporarily lag behind these specs while the token sync work is in progress. **Verify any token reference against the current code before depending on it.** See "Outstanding discrepancies" at the end of this skill for known gaps.
|
|
10
|
+
|
|
11
|
+
## Why spacing matters
|
|
12
|
+
|
|
13
|
+
Spacing is the invisible structure that holds an interface together. It creates relationships: grouping elements that belong together, separating elements that don't, and guiding a user's eye from one piece of content to the next. On Kiva, spacing shapes how borrowers read loan details, how lenders scan the stories, and how partners navigate their dashboards.
|
|
14
|
+
|
|
15
|
+
Common alternative names you may see used interchangeably: **padding**, **gap**, **layout**.
|
|
16
|
+
|
|
17
|
+
## Design principles
|
|
18
|
+
|
|
19
|
+
1. **Name the relationships.** Each spacing value should represent a clear intent — group related elements, separate distinct sections, indicate hierarchy, improve readability and scanning. *If you can't explain why a space exists, reconsider it.*
|
|
20
|
+
2. **Stay on the grid.** Every value is a 4px multiple. The spacing system shares its grid with typography and layout — reusing tokens across screens creates rhythm and reduces visual noise.
|
|
21
|
+
3. **Spacing signals hierarchy.** A larger space implies separation; a medium space implies "related but distinct"; a smaller space implies grouping. Hierarchy should be readable even with color and typography stripped away.
|
|
22
|
+
|
|
23
|
+
## The "between or inside" decision
|
|
24
|
+
|
|
25
|
+
When picking a token, start with the spatial relationship — not with a pixel value:
|
|
26
|
+
|
|
27
|
+
> **Am I spacing *between* things, or *inside* something?**
|
|
28
|
+
|
|
29
|
+
- **Between** → reach for **Structure** (page-scale) or **Component / Gap** (inside-a-component scale).
|
|
30
|
+
- **Inside** → reach for **Component / Inset** (padding within a bordered container).
|
|
31
|
+
|
|
32
|
+
The same number can mean two different things (16px as a card gap is not the same intent as 16px of internal card padding). Picking by category first keeps that distinction explicit.
|
|
33
|
+
|
|
34
|
+
## Semantic token categories
|
|
35
|
+
|
|
36
|
+
Token values are responsive: most tokens hold a single value across tiers, but a few shrink on mobile. Always apply the token, not a raw px — the responsiveness is the point.
|
|
37
|
+
|
|
38
|
+
### Structure
|
|
39
|
+
|
|
40
|
+
The large-scale spaces that shape a page's overall layout and breathing room. Use *between* sections and large compositions on a page.
|
|
41
|
+
|
|
42
|
+
| Token | Desktop | Tablet | Mobile | Example use |
|
|
43
|
+
|---|---|---|---|---|
|
|
44
|
+
| **XL** | 32px | 32px | 24px | Between major page sections (hero → content, content → footer) |
|
|
45
|
+
| **L** | 24px | 24px | 16px | Between large components |
|
|
46
|
+
| **M** | 16px | 16px | 16px | Between components |
|
|
47
|
+
| **S** | 8px | 8px | 8px | Between header and subheader |
|
|
48
|
+
|
|
49
|
+
### Component / Gap
|
|
50
|
+
|
|
51
|
+
Space between sibling elements *inside* a component. The internal breathing room of a component.
|
|
52
|
+
|
|
53
|
+
| Token | Desktop | Tablet | Mobile | Example use |
|
|
54
|
+
|---|---|---|---|---|
|
|
55
|
+
| **L** | 16px | 16px | 16px | Between cards in a grid |
|
|
56
|
+
| **M** | 8px | 8px | 8px | Between content and button inside a card; list-item internal spacing |
|
|
57
|
+
| **S** | 4px | 4px | 4px | Between tags or text elements in compact contexts |
|
|
58
|
+
|
|
59
|
+
### Component / Inset
|
|
60
|
+
|
|
61
|
+
Padding *inside* containers. Anything with a visible boundary — modals, cards, sidesheets, chips — gets inset padding.
|
|
62
|
+
|
|
63
|
+
| Token | Desktop | Tablet | Mobile | Example use |
|
|
64
|
+
|---|---|---|---|---|
|
|
65
|
+
| **XL** | 32px | 24px | 20px | Padding for modals, lightboxes, sidesheets, popups |
|
|
66
|
+
| **L** | 24px | 24px | 20px | Padding for large widgets, expanded panels, feature sections |
|
|
67
|
+
| **M** | 16px | 16px | 16px | Padding for medium cards, standard content containers |
|
|
68
|
+
| **S** | 8px | 8px | 8px | Padding for small cards, compact containers |
|
|
69
|
+
| **XS** | 4px | 4px | 4px | Padding for location chips, tags, badges |
|
|
70
|
+
|
|
71
|
+
### Micro
|
|
72
|
+
|
|
73
|
+
A single 4px value reserved for the **tightest inline coupling** — gaps that exist only to keep adjacent elements visually attached, not to imply hierarchy.
|
|
74
|
+
|
|
75
|
+
| Token | Desktop | Tablet | Mobile | Example use |
|
|
76
|
+
|---|---|---|---|---|
|
|
77
|
+
| **Micro** | 4px | 4px | 4px | Gap between icon + text pairs, small inline elements, gaps inside compact UI patterns |
|
|
78
|
+
|
|
79
|
+
## The raw spacing ramp
|
|
80
|
+
|
|
81
|
+
Underneath the semantic categories is a numeric ramp that is **4px-grained** — every token is a 4px multiple, and adjacent steps on the ramp are 4px apart. Every semantic token resolves to a value on this ramp, and the same ramp underpins layout values (gutters, margins) and any other dimension token in the system.
|
|
82
|
+
|
|
83
|
+
Token naming uses two kinds of step:
|
|
84
|
+
|
|
85
|
+
- **Whole-number tokens** (`1`, `2`, `3`…) land on the 8px multiples of the scale — `1` = 8px, `2` = 16px, `3` = 24px, etc. These are the most commonly reached-for values.
|
|
86
|
+
- **Half-step tokens** (`0-5`, `1-5`, `2-5`…) fill in the 4px steps between — `0-5` = 4px, `1-5` = 12px, `2-5` = 20px, etc.
|
|
87
|
+
|
|
88
|
+
Despite the `-5` naming, the half-steps are not "less canonical." They're first-class values on the same 4px scale. The Figma Spacing Ramp panel frames the system around an 8px base unit: *"The 8px base unit … forms the basis of our space token system, as the base unit `spacing 1`. Every space token is a multiple of this base unit."* That last claim is misleading — the half-step tokens (`0-5` = 4px, `1-5` = 12px, `2-5` = 20px) are **not** multiples of 8px. Treat the scale as **4px-grained** throughout, which matches both the Spacing Overview panel's "Every value is a 4px multiple" principle and the shipped code.
|
|
89
|
+
|
|
90
|
+
Quick formula: `N` = `N × 8px`, and `N-5` = `N × 8px + 4px` — both of which simplify to "the Nth and (N + ½)th rung of a 4px ramp."
|
|
91
|
+
|
|
92
|
+
| Token | px | rem | | Token | px | rem |
|
|
93
|
+
|---|---|---|---|---|---|---|
|
|
94
|
+
| 0 | 0 | 0 | | 8-5 | 68 | 4.25 |
|
|
95
|
+
| 0-5 | 4 | 0.25 | | 9 | 72 | 4.5 |
|
|
96
|
+
| 1 | 8 | 0.5 | | 9-5 | 76 | 4.75 |
|
|
97
|
+
| 1-5 | 12 | 0.75 | | 10 | 80 | 5 |
|
|
98
|
+
| 2 | 16 | 1 | | 10-5 | 84 | 5.25 |
|
|
99
|
+
| 2-5 | 20 | 1.25 | | 11 | 88 | 5.5 |
|
|
100
|
+
| 3 | 24 | 1.5 | | 11-5 | 92 | 5.75 |
|
|
101
|
+
| 3-5 | 28 | 1.75 | | 12 | 96 | 6 |
|
|
102
|
+
| 4 | 32 | 2 | | 12-5 | 100 | 6.25 |
|
|
103
|
+
| 4-5 | 36 | 2.25 | | 13 | 104 | 6.5 |
|
|
104
|
+
| 5 | 40 | 2.5 | | 13-5 | 108 | 6.75 |
|
|
105
|
+
| 5-5 | 44 | 2.75 | | 14 | 112 | 7 |
|
|
106
|
+
| 6 | 48 | 3 | | 14-5 | 116 | 7.25 |
|
|
107
|
+
| 6-5 | 52 | 3.25 | | 15 | 120 | 7.5 |
|
|
108
|
+
| 7 | 56 | 3.5 | | 15-5 | 124 | 7.75 |
|
|
109
|
+
| 7-5 | 60 | 3.75 | | 16 | 128 | 8 |
|
|
110
|
+
| 8 | 64 | 4 | | | | |
|
|
111
|
+
|
|
112
|
+
The raw ramp is the layer the **Tailwind config** ships today (e.g., `tw-p-2.5` = 20px). The semantic categories above are not yet exposed in code — see "Outstanding discrepancies."
|
|
113
|
+
|
|
114
|
+
> **Caveat about the Figma table:** the Spacing Scales & Tokens panel in Figma has typos in the `rem` column from row `10-5` onward (the values appear to have wrapped), and the panel labels the bottom two rows as `16 = 124px` and `16-5 = 128px` when they are actually `15-5` and `16` on the ramp. The shipped `tokens/core/size.json` is authoritative — trust it (and the formula above) over the rendered Figma table.
|
|
115
|
+
|
|
116
|
+
## Best practices
|
|
117
|
+
|
|
118
|
+
### Apply spacing by *meaning*, not values
|
|
119
|
+
|
|
120
|
+
- **Do** select spacing tokens based on their intended semantic purpose. Use **Component / Gap** for spacing between elements *inside* a component, and **Component / Inset** for the container's padding. Semantic consistency keeps layouts predictable, scalable, and easier to refactor.
|
|
121
|
+
- **Don't** substitute tokens across categories just because the pixel values happen to match. Picking by number alone breaks system logic and makes the system harder to evolve — when the value behind `Inset M` shifts later, you don't want stray `Gap M`s following along.
|
|
122
|
+
|
|
123
|
+
### A worked example (single card on a page)
|
|
124
|
+
|
|
125
|
+
Reading from outside in on a typical loan-style card:
|
|
126
|
+
|
|
127
|
+
1. **Structure XL** — space between the card section and the next page section.
|
|
128
|
+
2. **Component Inset XL** — padding inside the card.
|
|
129
|
+
3. **Component Inset L** — padding inside a sub-region of the card.
|
|
130
|
+
4. **Component Gap L** — vertical space between stacked content blocks inside the card (e.g., body → CTAs).
|
|
131
|
+
5. **Component Gap S** — tight space between a small heading and its supporting paragraph.
|
|
132
|
+
|
|
133
|
+
The mobile variant of the same card shifts insets down a tier (XL → 20px, L → 20px) and replaces Structure XL's 32 with 24 — all driven by the same tokens, automatically.
|
|
134
|
+
|
|
135
|
+
## Implementation in Figma
|
|
136
|
+
|
|
137
|
+
### Responsive spacing
|
|
138
|
+
|
|
139
|
+
Apply spacing tokens using Figma's **variable modes**. The token holds responsive values; switching modes refreshes everything bound to it.
|
|
140
|
+
|
|
141
|
+
1. Find **Appearance** in the right panel.
|
|
142
|
+
2. Click the component and select the variable mode (`lg` for desktop, `md` for tablet, `sm` for mobile).
|
|
143
|
+
3. When switching a frame's tier, change the mode to match the breakpoint.
|
|
144
|
+
4. Responsive tokens then update automatically.
|
|
145
|
+
|
|
146
|
+
### Finding the right token
|
|
147
|
+
|
|
148
|
+
Spacing tokens are organized by category in Figma's variable picker. Use the category to narrow results quickly.
|
|
149
|
+
|
|
150
|
+
- Search keywords: `gap`, `inset`, `structure`.
|
|
151
|
+
- Tokens are namespaced like `spacing/component/gap`, `spacing/component/inset`, `spacing/structure`.
|
|
152
|
+
- If you're unsure which category fits, ask the "between or inside" question first.
|
|
153
|
+
|
|
154
|
+
### Before handoff
|
|
155
|
+
|
|
156
|
+
Check auto-layout properties on key frames. If you see a raw number instead of a variable name, **re-bind it from the library** — a raw value is a detached spacing decision that won't follow updates and gives developers nothing to map to.
|
|
157
|
+
|
|
158
|
+
## Usage rules
|
|
159
|
+
|
|
160
|
+
### Do
|
|
161
|
+
|
|
162
|
+
- Pick the token by **category first**, then size.
|
|
163
|
+
- Apply tokens via Figma variables (or, in code, via the Tailwind utility) — never as raw px in an inline style.
|
|
164
|
+
- Let responsive tokens do the per-tier work; don't author one-off mobile values when an existing token already encodes the shift.
|
|
165
|
+
- Re-use the same token for spaces that mean the same thing across screens, even when the surrounding components differ.
|
|
166
|
+
|
|
167
|
+
### Don't
|
|
168
|
+
|
|
169
|
+
- Don't substitute tokens across categories because the pixel value happens to match (`Inset M` ≠ `Gap M` even if both are 16px today).
|
|
170
|
+
- Don't reach for a raw `tw-p-[18px]` or `gap: 18px` — if no token fits, that's a design-system conversation, not a one-off override.
|
|
171
|
+
- Don't mix `Micro` with the other categories for hierarchy. Micro is for *coupling*, not separation.
|
|
172
|
+
- Don't leave detached spacing values in Figma at handoff.
|
|
173
|
+
|
|
174
|
+
### Need something not covered here?
|
|
175
|
+
|
|
176
|
+
Open a request with the design system team. Include the use case, the component, and why the existing categories or tokens don't fit.
|
|
177
|
+
|
|
178
|
+
## Using with Tailwind
|
|
179
|
+
|
|
180
|
+
The spacing utilities come from the `@kiva/kv-tokens` Tailwind preset. Haven't registered it yet? See [tailwind → Consuming the preset](tailwind.md#consuming-the-preset). Not using the preset? See [Without the preset](#without-the-preset) below.
|
|
181
|
+
|
|
182
|
+
The preset wires every value on [the raw spacing ramp](#the-raw-spacing-ramp) into the standard spacing utilities — `tw-p-*`, `tw-m-*`, `tw-gap-*`, `tw-space-*`, etc. all flow from the `space` scale. The thing to internalize: this is an **8px scale with 4px half-steps**, so the same number means a different size than stock Tailwind — `tw-p-4` is **32px** here, not 16px. See [tailwind → Spacing is an 8px scale](tailwind.md#spacing-is-an-8px-scale-with-4px-half-steps).
|
|
183
|
+
|
|
184
|
+
Three key-naming conventions describe the same tokens — don't get tripped up:
|
|
185
|
+
|
|
186
|
+
- **Tailwind classes** use dots: `tw-p-0.5`, `tw-p-1`, `tw-p-1.5`, `tw-p-2.5` (4px, 8px, 12px, 20px).
|
|
187
|
+
- **`tokens/core/size.json`** (the authoritative source) uses underscores: `0_5`, `1`, `1_5`, `2_5`.
|
|
188
|
+
- **Figma's variable picker** uses hyphens: `0-5`, `1`, `1-5`, `2-5`.
|
|
189
|
+
|
|
190
|
+
The shipped code exposes this **raw numeric ramp**, not the semantic categories above — verify a value against `@kiva/kv-tokens/tokens/core/size.json` before depending on it. Express responsive shifts with breakpoint prefixes (`tw-p-2.5 md:tw-p-3 lg:tw-p-4`); note there is **no `sm` screen** — mobile is the unprefixed default.
|
|
191
|
+
|
|
192
|
+
### Without the preset
|
|
193
|
+
|
|
194
|
+
- **Kiva (or Kiva-adjacent) repo, preset not registered yet:** install and register it — [tailwind → Consuming the preset](tailwind.md#consuming-the-preset).
|
|
195
|
+
- **Stock-Tailwind / non-Kiva project:** the ramp is plain px, so the values in [the raw spacing ramp](#the-raw-spacing-ramp) table can be applied as arbitrary values (`p-[20px]`) or used to define your own scale. These are point-in-time copies.
|
|
196
|
+
|
|
197
|
+
## Outstanding discrepancies
|
|
198
|
+
|
|
199
|
+
- **No shipped semantic spacing tokens.** `structure-xl`, `component-gap-l`, `component-inset-xl`, `micro`, etc. do not exist in code — the semantic layer lives only in Figma variables today. In code, express the intent by picking the matching ramp value (optionally with a comment or a thin component wrapper).
|
|
200
|
+
- **No responsive spacing tokens.** Figma encodes per-tier shifts (e.g. `Inset XL` is `32 / 24 / 20`); the code does not. Today, responsive shifts must be done with Tailwind responsive prefixes.
|
|
201
|
+
|
|
202
|
+
When you find a divergence between this skill and the shipped tokens, flag it — each is a data point for the design-system team.
|
|
203
|
+
|
|
204
|
+
## Figma source references
|
|
205
|
+
|
|
206
|
+
- Spacing Overview: node `17285:6968`
|
|
207
|
+
- Spacing Scales & Tokens: node `17285:6976`
|
|
208
|
+
- Spacing Guidelines: node `17285:6981`
|
|
209
|
+
|
|
210
|
+
File: `TPmBUB4olYPMF6glEhBGDG` (Ecosystem 2026 — WIP)
|