@junoput01/junoui 0.2.0 → 0.3.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/CHANGELOG.md +32 -0
- package/dist/css/juno.css +162 -3
- package/docs/accessibility.md +167 -0
- package/docs/components/README.md +68 -0
- package/docs/components/accordion.md +49 -0
- package/docs/components/alert.md +46 -0
- package/docs/components/avatar.md +45 -0
- package/docs/components/badge.md +35 -0
- package/docs/components/breadcrumb.md +31 -0
- package/docs/components/button.md +32 -0
- package/docs/components/card.md +41 -0
- package/docs/components/checkbox.md +41 -0
- package/docs/components/chip.md +44 -0
- package/docs/components/divider.md +32 -0
- package/docs/components/dock.md +56 -0
- package/docs/components/drawer.md +60 -0
- package/docs/components/field.md +44 -0
- package/docs/components/gauge.md +48 -0
- package/docs/components/icon-loader.md +55 -0
- package/docs/components/icon.md +49 -0
- package/docs/components/input.md +45 -0
- package/docs/components/list.md +76 -0
- package/docs/components/loader.md +80 -0
- package/docs/components/menu.md +54 -0
- package/docs/components/modal.md +52 -0
- package/docs/components/navbar.md +53 -0
- package/docs/components/pagination.md +42 -0
- package/docs/components/pillbar.md +64 -0
- package/docs/components/popover.md +43 -0
- package/docs/components/rail.md +54 -0
- package/docs/components/readout.md +39 -0
- package/docs/components/segmented.md +46 -0
- package/docs/components/select.md +33 -0
- package/docs/components/skeleton.md +38 -0
- package/docs/components/slider.md +48 -0
- package/docs/components/spark.md +51 -0
- package/docs/components/status.md +30 -0
- package/docs/components/stepper.md +46 -0
- package/docs/components/switch.md +38 -0
- package/docs/components/table.md +126 -0
- package/docs/components/tabs.md +61 -0
- package/docs/components/thumb.md +61 -0
- package/docs/components/toast.md +48 -0
- package/docs/components/toggle-button.md +40 -0
- package/docs/components/tooltip.md +63 -0
- package/docs/design-guidelines.md +147 -0
- package/docs/flutter.md +42 -0
- package/docs/getting-started.md +70 -0
- package/docs/integration.md +95 -0
- package/docs/layout.md +201 -0
- package/docs/native.md +52 -0
- package/docs/roadmap.md +106 -0
- package/docs/tokens-reference.md +223 -0
- package/docs/web.md +89 -0
- package/package.json +3 -2
- package/src/css/base.css +7 -0
- package/src/css/components/dock.css +13 -0
- package/src/css/components/icon-loader.css +53 -0
- package/src/css/components/pillbar.css +15 -0
- package/src/css/components/rail.css +8 -0
- package/src/css/layout.css +65 -3
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Design guidelines
|
|
2
|
+
|
|
3
|
+
Buildable in any tool — these are rules, not code. Pair with exact values in
|
|
4
|
+
[tokens-reference.md](./tokens-reference.md).
|
|
5
|
+
|
|
6
|
+
## First principle: color encodes status, never decoration
|
|
7
|
+
|
|
8
|
+
Every hue has one assigned role. Never reuse a status color as styling.
|
|
9
|
+
|
|
10
|
+
| Role | Meaning | When |
|
|
11
|
+
| --------- | ----------------- | ----------------------------------------------------------------------- |
|
|
12
|
+
| `nominal` | Normal / OK | Nominal readings, confirmed-good, success. |
|
|
13
|
+
| `active` | Selected / active | Current selection, armed state, focus rings, live status. |
|
|
14
|
+
| `target` | Managed / goal | Computed set-points, automated targets. Use sparingly. |
|
|
15
|
+
| `caution` | Non-normal, watch | Approaching limits, degraded — no immediate action. |
|
|
16
|
+
| `warning` | Critical | Limit exceeded, act now. Reserve strictly — overuse destroys authority. |
|
|
17
|
+
|
|
18
|
+
Neutral / structural roles carry no status meaning:
|
|
19
|
+
|
|
20
|
+
| Role | Use |
|
|
21
|
+
| --------------- | ------------------------------------------------------------------ |
|
|
22
|
+
| `data` | Primary numeric readouts and live values — the brightest text. |
|
|
23
|
+
| `data-dim` | Faint metadata — timestamps, tick labels. Decorative; below muted. |
|
|
24
|
+
| `label` | Labels, units, helper and secondary text. Never for values. |
|
|
25
|
+
| `muted` | Disabled, placeholder, decorative separators. |
|
|
26
|
+
| `border` | Hairline borders and dividers. |
|
|
27
|
+
| `border-strong` | Divider / stronger rule — heavier than the hairline border. |
|
|
28
|
+
| `s0`–`s3` | Surface depth: base → panel → elevated card → selected/overlay. |
|
|
29
|
+
|
|
30
|
+
## More principles
|
|
31
|
+
|
|
32
|
+
- **Hierarchy via contrast, not scale.** Brightness/weight separate levels before size does.
|
|
33
|
+
- **Density is intentional.** Choose spacing deliberately; don't pad by accident.
|
|
34
|
+
Switch the whole UI between comfortable and compact with one attribute (below).
|
|
35
|
+
- **Designed for long sessions.** The `soft` palette and dark mode reduce fatigue.
|
|
36
|
+
|
|
37
|
+
## Typography
|
|
38
|
+
|
|
39
|
+
- **B612 Mono** — every number, timestamp, identifier, code. Fixed-width digits
|
|
40
|
+
stop value-change jitter. Use `tabular-nums`.
|
|
41
|
+
- **B612** — all non-numeric UI: headings, labels, navigation, buttons.
|
|
42
|
+
- Headings are uppercase with wide tracking; values are mono and bright (`data`).
|
|
43
|
+
|
|
44
|
+
## Date & time
|
|
45
|
+
|
|
46
|
+
One fixed house format everywhere — no locale drift between screens. This is a
|
|
47
|
+
**content convention**, not code: apps do the formatting; junoui specifies what it
|
|
48
|
+
looks like.
|
|
49
|
+
|
|
50
|
+
| What | Format | Example |
|
|
51
|
+
| -------- | ------------------------- | -------------------- |
|
|
52
|
+
| Date | `dd.mm.yyyy`, zero-padded | `05.07.2026` |
|
|
53
|
+
| Time | 24-hour `HH:MM`(`:SS`) | `14:32` · `14:32:07` |
|
|
54
|
+
| Combined | date first, interpunct | `05.07.2026 · 14:32` |
|
|
55
|
+
| Range | en-dash, no spaces | `14:00–15:30` |
|
|
56
|
+
| Open | trailing en-dash | `14:00–` |
|
|
57
|
+
|
|
58
|
+
Rendering rules:
|
|
59
|
+
|
|
60
|
+
- Always mono + `tabular-nums` (the [Typography](#typography) value rule) — use
|
|
61
|
+
`.juno-value` or `.juno-mono`, wrapped in `<time>` with the machine-readable ISO
|
|
62
|
+
value: `<time class="juno-mono" datetime="2026-07-05T14:32">05.07.2026 · 14:32</time>`.
|
|
63
|
+
- Never AM/PM, never month names in data UI (prose may spell them out).
|
|
64
|
+
- Relative time ("2h ago") is app policy; when used, pair it with the absolute
|
|
65
|
+
timestamp in a `title` or adjacent text.
|
|
66
|
+
|
|
67
|
+
App-side recipe (the `de-DE` locale happens to produce exactly this format):
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
const fmt = new Intl.DateTimeFormat('de-DE', {
|
|
71
|
+
day: '2-digit',
|
|
72
|
+
month: '2-digit',
|
|
73
|
+
year: 'numeric',
|
|
74
|
+
hour: '2-digit',
|
|
75
|
+
minute: '2-digit',
|
|
76
|
+
hour12: false,
|
|
77
|
+
});
|
|
78
|
+
fmt.format(date).replace(', ', ' · '); // "05.07.2026 · 14:32"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
This is the junoui house format. Locale-sensitive consumer products may override it
|
|
82
|
+
with `Intl` defaults — deviating is a per-app decision; log it like any other
|
|
83
|
+
extension (see [integration.md](./integration.md)).
|
|
84
|
+
|
|
85
|
+
## Foundation tokens (motion, layering, depth, opacity)
|
|
86
|
+
|
|
87
|
+
These scales exist so transitions, overlays, and surfaces stay consistent. junoui
|
|
88
|
+
ships the values; stateful behavior (open/close, positioning, focus traps) lives in
|
|
89
|
+
apps. Exact values: [tokens-reference.md](./tokens-reference.md).
|
|
90
|
+
|
|
91
|
+
- **Motion.** Durations `--juno-motion-duration-{instant,quick,base,deliberate}`
|
|
92
|
+
(80·140·200·300ms) and easings `--juno-motion-ease-{decel,accel,standard,spring}`.
|
|
93
|
+
`instant` for state feedback, `quick` for tooltip/menu, `base` for popover/drawer,
|
|
94
|
+
`deliberate` for modal/scrim. Enter with `decel`, exit with `accel`, reposition with
|
|
95
|
+
`standard`, toggle with `spring`. Always inside a `transition`/`animation`;
|
|
96
|
+
`prefers-reduced-motion` already collapses these in `base.css`.
|
|
97
|
+
- **Z-index.** `--juno-z-*` is the single layering source of truth — never invent raw
|
|
98
|
+
z-index. Layer stack: `surface` (0) < `raised` (100) < `anchored` (2000, menu ·
|
|
99
|
+
popover · tooltip) < `overlay` (4000, modal · drawer + scrim) < `alert` (5000, toast).
|
|
100
|
+
Large gaps leave room for app layers.
|
|
101
|
+
- **Elevation.** Depth is **border-first** by design; elevation = z-index + shadow,
|
|
102
|
+
paired. Reach for `--juno-shadow-{1,2,3}` only when a surface lifts off the glass —
|
|
103
|
+
`1` raised card, `2` anchored (menu/popover/tooltip), `3` overlay (modal/drawer).
|
|
104
|
+
- **Opacity.** `--juno-opacity-disabled` for inert controls, `--juno-opacity-muted`
|
|
105
|
+
for de-emphasised content, `--juno-opacity-scrim` for a modal/drawer backdrop.
|
|
106
|
+
|
|
107
|
+
## Density
|
|
108
|
+
|
|
109
|
+
One attribute on any ancestor swaps the internal padding of every component
|
|
110
|
+
underneath it — no per-component class:
|
|
111
|
+
|
|
112
|
+
```html
|
|
113
|
+
<body data-juno-density="compact">
|
|
114
|
+
<!-- comfortable (default) needs no attribute -->
|
|
115
|
+
</body>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Components read **semantic padding aliases** (`--juno-pad-control-*`,
|
|
119
|
+
`--juno-pad-surface-*`, `--juno-gap-control`) instead of raw `--juno-space-*`;
|
|
120
|
+
`data-juno-density` redefines that set. Density is deliberately **non-linear** —
|
|
121
|
+
compact removes more block (vertical) than inline padding, so text never crowds its
|
|
122
|
+
edges. Interactive controls keep their `min-height` (WCAG tap target); only padding
|
|
123
|
+
shrinks. New components should use the aliases for internal padding to inherit
|
|
124
|
+
density for free; add a new archetype only when one is genuinely needed.
|
|
125
|
+
|
|
126
|
+
## Accessibility
|
|
127
|
+
|
|
128
|
+
Accessibility is a core design goal, held to published standards — **WCAG 2.2** and
|
|
129
|
+
**WAI-ARIA** — with the exact success criteria cited in
|
|
130
|
+
[accessibility.md](./accessibility.md).
|
|
131
|
+
|
|
132
|
+
- The `colorblind` palette is the IBM Carbon universal set — distinguishable across
|
|
133
|
+
deuteranopia, protanopia, tritanopia, and AAA-contrast (≥ 7:1) on dark surfaces
|
|
134
|
+
(WCAG 1.4.6).
|
|
135
|
+
- **Never make color the sole signal** (WCAG 1.4.1). Always pair it with a text label,
|
|
136
|
+
icon, or shape.
|
|
137
|
+
- Interactive targets: `size.tap.min` = 24px (WCAG 2.5.8, AA); `size.tap.comfortable`
|
|
138
|
+
= 44px (WCAG 2.5.5, AAA) recommended for primary mobile actions.
|
|
139
|
+
- Focus is always visible — the `active` role, 2px outline (WCAG 2.4.7 / 2.4.11).
|
|
140
|
+
|
|
141
|
+
## Choosing a palette
|
|
142
|
+
|
|
143
|
+
| Palette | Use |
|
|
144
|
+
| ------------ | -------------------------------------------------- |
|
|
145
|
+
| `standard` | Default. Vivid, high-contrast OKLCH. |
|
|
146
|
+
| `colorblind` | Accessibility-critical or universal audiences. |
|
|
147
|
+
| `soft` | Long viewing sessions; lower chroma, less fatigue. |
|
package/docs/flutter.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Flutter
|
|
2
|
+
|
|
3
|
+
Colors are pre-converted to sRGB hex at build time (Dart has no `oklch`).
|
|
4
|
+
|
|
5
|
+
## Add the file
|
|
6
|
+
|
|
7
|
+
Copy the generated file into your project:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
dist/flutter/juno_tokens.dart → lib/theme/juno_tokens.dart
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Use it
|
|
14
|
+
|
|
15
|
+
```dart
|
|
16
|
+
import 'theme/juno_tokens.dart';
|
|
17
|
+
|
|
18
|
+
Container(
|
|
19
|
+
color: JunoTokens.standardDarkS1,
|
|
20
|
+
padding: const EdgeInsets.all(JunoTokens.space16),
|
|
21
|
+
child: Text(
|
|
22
|
+
'89.3',
|
|
23
|
+
style: TextStyle(
|
|
24
|
+
color: JunoTokens.standardDarkNominal,
|
|
25
|
+
fontSize: JunoTokens.fontSize38,
|
|
26
|
+
fontFamily: 'B612 Mono',
|
|
27
|
+
),
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- Colors are `Color` constants named `<palette><Mode><Role>` (camelCase),
|
|
33
|
+
fully opaque (`0xFF…`).
|
|
34
|
+
- Dimensions are `double` constants (`space16`, `fontSize14`, `radius8`, …).
|
|
35
|
+
|
|
36
|
+
## Theming
|
|
37
|
+
|
|
38
|
+
Map the active palette/mode to a helper that returns the right constant, or build a
|
|
39
|
+
`ThemeExtension` keyed on palette+mode. All constants derive from the same source,
|
|
40
|
+
so they stay in lockstep with web and native.
|
|
41
|
+
|
|
42
|
+
Exact values for every token: [tokens-reference.md](./tokens-reference.md).
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
junoui is a design system for **modern, futuristic, highly readable and accessible**
|
|
4
|
+
UIs — usable for critical systems and everyday apps alike. Mechanically it is the
|
|
5
|
+
single graphics source for your UIs: colors, spacing, typography, radii, sizing. You
|
|
6
|
+
build the UI skeleton; junoui defines how it looks. Tokens are authored once in
|
|
7
|
+
`tokens/` and compiled to every platform.
|
|
8
|
+
|
|
9
|
+
Accessibility is a first-class goal, held to the international standards (WCAG 2.2 +
|
|
10
|
+
WAI-ARIA) with the exact criteria cited per component — see
|
|
11
|
+
[accessibility.md](./accessibility.md).
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm install @junoput01/junoui
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or as a git submodule (then build once):
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
git submodule add git@github.com:junoput/junoui.git vendor/junoui
|
|
23
|
+
cd vendor/junoui && npm install # `prepare` builds dist/
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`npm install` runs the `prepare` script, so `dist/` is always built for you.
|
|
27
|
+
|
|
28
|
+
## The model
|
|
29
|
+
|
|
30
|
+
- **Palette** — `standard` · `colorblind` · `soft`
|
|
31
|
+
- **Mode** — `dark` · `light`
|
|
32
|
+
- **Role** — `nominal` `active` `target` `caution` `warning` (semantic) and
|
|
33
|
+
`data` `label` `muted` `border` `s0`–`s3` (neutral/surface)
|
|
34
|
+
|
|
35
|
+
A theme is one palette × one mode. Color always encodes status — see
|
|
36
|
+
[design-guidelines.md](./design-guidelines.md).
|
|
37
|
+
|
|
38
|
+
## Pick your platform
|
|
39
|
+
|
|
40
|
+
| You're building in… | Read |
|
|
41
|
+
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
42
|
+
| Web (CSS, SCSS, JS/TS) | [web.md](./web.md) |
|
|
43
|
+
| Android / iOS | [native.md](./native.md) |
|
|
44
|
+
| Flutter | [flutter.md](./flutter.md) |
|
|
45
|
+
| Figma / Sketch / any tool without code | [tokens-reference.md](./tokens-reference.md) (exact values) + [design-guidelines.md](./design-guidelines.md) |
|
|
46
|
+
|
|
47
|
+
Responsive behavior (how blocks adapt on resize) is its own layer:
|
|
48
|
+
[layout.md](./layout.md).
|
|
49
|
+
|
|
50
|
+
## What ships
|
|
51
|
+
|
|
52
|
+
`npm install @junoput01/junoui` delivers `dist/` (all platform outputs), `src/css/` (authored
|
|
53
|
+
stylesheet sources), `src/icons/` (SVG icon sources + license), and `src/fonts/`
|
|
54
|
+
(self-hosted B612 woff2 + OFL license). The interactive demo in `showcase/` is **not**
|
|
55
|
+
shipped — it lives in the repo for development only.
|
|
56
|
+
|
|
57
|
+
## Build outputs
|
|
58
|
+
|
|
59
|
+
| Path | Platform |
|
|
60
|
+
| ----------------------------------------- | ------------------------------------------------------------- |
|
|
61
|
+
| `dist/css/juno.css` | Web — tokens + base + utilities + components |
|
|
62
|
+
| `dist/css/juno-tokens.css` | Web — CSS variables only |
|
|
63
|
+
| `dist/css/juno-fonts.css` + `dist/fonts/` | Web — opt-in self-hosted B612 (`@junoput01/junoui/fonts.css`) |
|
|
64
|
+
| `dist/icons/juno-icons.svg` | Web — icon sprite (`@junoput01/junoui/icons`) |
|
|
65
|
+
| `dist/scss/_juno-tokens.scss` | Web — SCSS variables |
|
|
66
|
+
| `dist/js/tokens.js` + `.d.ts` | JS / TS — `TOKENS`, `CORE`, `getTokens()` |
|
|
67
|
+
| `dist/json/tokens.json` | W3C DTCG — any tool |
|
|
68
|
+
| `dist/android/colors.xml` · `dimens.xml` | Android |
|
|
69
|
+
| `dist/ios/JunoTokens.swift` | iOS |
|
|
70
|
+
| `dist/flutter/juno_tokens.dart` | Flutter |
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Integrating junoui into an app
|
|
2
|
+
|
|
3
|
+
junoui is **presentational**: tokens + a CSS component layer, zero JS. It dresses a UI
|
|
4
|
+
your app builds. This is the recipe for consuming it cleanly — and the rules for
|
|
5
|
+
extending it without breaking the "one design across all apps" goal.
|
|
6
|
+
|
|
7
|
+
## 1. Import order (app shell)
|
|
8
|
+
|
|
9
|
+
`@junoput01/junoui/css` sets a small reset + document defaults on `:root`/`body`. Load it **first**,
|
|
10
|
+
your app styles **after**, so your shell wins where it must:
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
import '@junoput01/junoui/css'; // tokens + base + components — FIRST
|
|
14
|
+
import '@junoput01/junoui/fonts.css'; // optional self-hosted B612 (see §2)
|
|
15
|
+
import './app.css'; // your shell + app-specific styles — AFTER
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
What junoui's base layer touches: `box-sizing`, margin/padding reset, `body`
|
|
19
|
+
font/color/background, link color, focus-visible ring, and `prefers-reduced-motion` /
|
|
20
|
+
`forced-colors` handling. It does **not** position or lay out your shell — that's yours.
|
|
21
|
+
If a body default fights your shell, override it in your app CSS (loaded after).
|
|
22
|
+
|
|
23
|
+
## 2. Fonts (opt-in, CSP-safe)
|
|
24
|
+
|
|
25
|
+
junoui never fetches fonts. Either bring your own B612, or opt into the self-hosted set:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
import '@junoput01/junoui/fonts.css'; // local @font-face → woff2 in the package, no network
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Safe under a strict CSP (`font-src 'self'`). Skip it and the UI falls back to system
|
|
32
|
+
sans/mono until you provide the family.
|
|
33
|
+
|
|
34
|
+
## 3. Theming — two attributes
|
|
35
|
+
|
|
36
|
+
```html
|
|
37
|
+
<html data-juno-palette="standard" data-juno-mode="dark" data-juno-density="comfortable"></html>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- `data-juno-palette`: `standard` · `colorblind` · `soft`
|
|
41
|
+
- `data-juno-mode`: `dark` · `light`
|
|
42
|
+
- `data-juno-density`: `comfortable` · `compact`
|
|
43
|
+
- `data-juno-text`: `base` · `large` · `xl` (type scale)
|
|
44
|
+
|
|
45
|
+
Flip these at runtime and the whole UI re-themes. Persist them if you want (localStorage);
|
|
46
|
+
the value is yours to manage.
|
|
47
|
+
|
|
48
|
+
## 4. Token bridge (aliasing app variables)
|
|
49
|
+
|
|
50
|
+
If your app already has design aliases, redefine them in terms of `--juno-*` so one system
|
|
51
|
+
drives everything:
|
|
52
|
+
|
|
53
|
+
```css
|
|
54
|
+
:root {
|
|
55
|
+
--bg: var(--juno-s0);
|
|
56
|
+
--ink: var(--juno-data);
|
|
57
|
+
--ink-dim: var(--juno-data-dim); /* timestamps, metadata */
|
|
58
|
+
--line: var(--juno-border);
|
|
59
|
+
--line-strong: var(--juno-border-strong); /* dividers */
|
|
60
|
+
--accent: var(--juno-active); /* see §5 — accent is semantic */
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Prefer a shipped token over `color-mix()`. junoui exposes the full neutral ramp
|
|
65
|
+
(`data` · `data-dim` · `label` · `muted`) and two border tones (`border` ·
|
|
66
|
+
`border-strong`), so most dense-UI needs don't require mixing.
|
|
67
|
+
|
|
68
|
+
## 5. Accent is semantic — not a brand hue
|
|
69
|
+
|
|
70
|
+
junoui has **no decorative brand-accent knob**, on purpose. Color encodes _status_, never
|
|
71
|
+
decoration: every hue is a role (`nominal` · `active` · `target` · `caution` · `warning`).
|
|
72
|
+
The "accent" is the `active` role, and it changes with the palette.
|
|
73
|
+
|
|
74
|
+
If you want a different accent, **switch palette** (`soft` reads calmer) or add your own
|
|
75
|
+
brand color as an app-local variable used only for non-semantic chrome (a logo, a splash).
|
|
76
|
+
Do not remap junoui's roles to a brand hue — that erases the shared meaning across apps.
|
|
77
|
+
|
|
78
|
+
## 6. Extending — where new things go
|
|
79
|
+
|
|
80
|
+
| Need | Where |
|
|
81
|
+
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
82
|
+
| New **status meaning**, reusable across apps | Propose a new role in junoui core (semver: additive = minor). Rare. |
|
|
83
|
+
| A full **alternate theme** (same role names, new values) | A new **palette** (like `standard`/`colorblind`/`soft`) — the sanctioned expansion slot. |
|
|
84
|
+
| App-specific **decorative** color | App-local, **namespaced** (`--myapp-*`), layered on top; alias to `--juno-*` where you can. Never redefine junoui roles. |
|
|
85
|
+
| A **component** junoui lacks (e.g. chat bubble) | Build it locally against the token contract until junoui (or a `junoui-<domain>` layer) ships it. |
|
|
86
|
+
|
|
87
|
+
Rule of thumb: extend **additively and namespaced**. If every app injects its own palette
|
|
88
|
+
into junoui, the single-design guarantee dies.
|
|
89
|
+
|
|
90
|
+
## 7. Stateful behavior stays in your app
|
|
91
|
+
|
|
92
|
+
junoui ships no JS. Focus traps, list virtualization, popover positioning, data — all
|
|
93
|
+
yours (or a future sibling `junoui-<framework>` package). junoui gives you the stable,
|
|
94
|
+
semantic DOM (real elements, BEM classes, ARIA hooks) to wire behavior + analytics onto;
|
|
95
|
+
see [accessibility.md](./accessibility.md) for the per-component ARIA contract.
|
package/docs/layout.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Layout & responsiveness
|
|
2
|
+
|
|
3
|
+
How blocks adapt — defined once, inherited by every UI. Two mechanisms:
|
|
4
|
+
|
|
5
|
+
1. **Intrinsic layout primitives** — reflow by available space, no media queries.
|
|
6
|
+
2. **Container queries** — a component restyles by _its own_ width, not the viewport
|
|
7
|
+
(right for a library dropped into unknown layouts).
|
|
8
|
+
|
|
9
|
+
Breakpoint tokens (`--juno-bp-sm…2xl`) follow Tailwind's scale and exist for the
|
|
10
|
+
rare viewport-level case.
|
|
11
|
+
|
|
12
|
+
## Why container queries over viewport breakpoints
|
|
13
|
+
|
|
14
|
+
junoui components live inside arbitrary app skeletons. A card doesn't know if it's
|
|
15
|
+
in a 280px sidebar or full width — so it adapts to its container:
|
|
16
|
+
|
|
17
|
+
```css
|
|
18
|
+
.juno-card {
|
|
19
|
+
container-type: inline-size;
|
|
20
|
+
} /* already set */
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`.juno-card__row` stacks automatically when the card is < 320px wide, anywhere.
|
|
24
|
+
|
|
25
|
+
## Layout primitives
|
|
26
|
+
|
|
27
|
+
Wrap a region; it adapts on resize. Every knob is a custom property with a token
|
|
28
|
+
default — override per instance inline.
|
|
29
|
+
|
|
30
|
+
| Class | Does | Key knob (default) |
|
|
31
|
+
| ------------------------ | ---------------------------------------- | ---------------------------------------- |
|
|
32
|
+
| `.juno-center` | Max-measure wrapper, fluid gutters | `--juno-measure` (`bp-xl`) |
|
|
33
|
+
| `.juno-stack` | Vertical rhythm between children | `--juno-stack-space` (`space-16`) |
|
|
34
|
+
| `.juno-cluster` | Wrapping inline group (toolbars, tags) | `--juno-cluster-space` (`space-8`) |
|
|
35
|
+
| `.juno-grid-auto` | Cards that collapse columns themselves | `--juno-grid-min` (`240px`) |
|
|
36
|
+
| `.juno-grid-auto--tiles` | Media wall wired to the density layer | `--juno-tile-min` / `--juno-gap-content` |
|
|
37
|
+
| `.juno-sidebar` | Aside + fluid content, stacks when tight | `--juno-sidebar-width` (`280px`) |
|
|
38
|
+
| `.juno-switcher` | N-up or all-stacked at a threshold | `--juno-switcher-threshold` (`bp-sm`) |
|
|
39
|
+
| `.juno-reel` | Horizontal scroll-snap row | `--juno-reel-space` (`space-12`) |
|
|
40
|
+
|
|
41
|
+
### Examples
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<!-- responsive card grid — no breakpoints, columns just fit -->
|
|
45
|
+
<div class="juno-grid-auto" style="--juno-grid-min:280px;">
|
|
46
|
+
<article class="juno-card">…</article>
|
|
47
|
+
<article class="juno-card">…</article>
|
|
48
|
+
<article class="juno-card">…</article>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- toolbar that wraps -->
|
|
52
|
+
<div class="juno-cluster">
|
|
53
|
+
<button class="juno-btn juno--nominal">CONFIRM</button>
|
|
54
|
+
<button class="juno-btn juno-btn--ghost">CANCEL</button>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<!-- sidebar + content; stacks when content would get too narrow -->
|
|
58
|
+
<div class="juno-sidebar">
|
|
59
|
+
<aside class="juno-sidebar__aside">…</aside>
|
|
60
|
+
<main class="juno-sidebar__main">…</main>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<!-- two-up that flips to stacked under ~640px of space -->
|
|
64
|
+
<div class="juno-switcher">
|
|
65
|
+
<div>A</div>
|
|
66
|
+
<div>B</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- media wall: tile size + gap follow [data-juno-density] -->
|
|
70
|
+
<div class="juno-grid-auto juno-grid-auto--tiles"><img … /><img … /><img … /></div>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`--tiles` reads `--juno-tile-min` / `--juno-gap-content` from the density layer
|
|
74
|
+
(`150px` / `space-10` comfortable, `108px` / `space-4` compact), so one
|
|
75
|
+
`data-juno-density` attribute re-densifies controls, surfaces **and** content
|
|
76
|
+
grids together.
|
|
77
|
+
|
|
78
|
+
## App shell
|
|
79
|
+
|
|
80
|
+
Every product app assembles the same frame; `.juno-app-shell` ships it as
|
|
81
|
+
classes — a collapsible [rail](./components/rail.md), a topbar, a scrolling
|
|
82
|
+
content outlet, and a [dock](./components/dock.md) at the foot — so you stop
|
|
83
|
+
copy-pasting the same `<style>` block into every app.
|
|
84
|
+
|
|
85
|
+
```html
|
|
86
|
+
<div class="juno-app-shell">
|
|
87
|
+
<nav class="juno-rail juno-rail--responsive" aria-label="Primary">
|
|
88
|
+
<div class="juno-rail__brand">JUNO</div>
|
|
89
|
+
<a class="juno-rail__item" href="/library" aria-current="page">
|
|
90
|
+
<svg class="juno-icon" aria-hidden="true"><use href="…#juno-i-squares-four" /></svg>
|
|
91
|
+
<span class="juno-rail__label">Library</span>
|
|
92
|
+
</a>
|
|
93
|
+
</nav>
|
|
94
|
+
<div class="juno-app-shell__body">
|
|
95
|
+
<header class="juno-app-shell__topbar">
|
|
96
|
+
<input class="juno-input" type="search" placeholder="SEARCH…" />
|
|
97
|
+
<span class="juno-badge juno-badge--soft juno--nominal">ONLINE</span>
|
|
98
|
+
</header>
|
|
99
|
+
<main class="juno-app-shell__main">…</main>
|
|
100
|
+
<nav class="juno-dock juno-hide-from-md" aria-label="Primary">
|
|
101
|
+
<a class="juno-dock__item" href="/library" aria-current="page">
|
|
102
|
+
<svg class="juno-icon" aria-hidden="true"><use href="…#juno-i-squares-four" /></svg>
|
|
103
|
+
<span class="juno-dock__label">Library</span>
|
|
104
|
+
</a>
|
|
105
|
+
</nav>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
What the primitive encodes so you don't have to:
|
|
111
|
+
|
|
112
|
+
- **`100dvh`, not `100vh`** — the shell fills the _dynamic_ viewport, so the
|
|
113
|
+
dock isn't clipped by the phone's shrinking address bar.
|
|
114
|
+
- **The `__main` region is the scroller**, not the page. The dock (or
|
|
115
|
+
[pillbar](./components/pillbar.md)) is a flex sibling at the body foot, so it
|
|
116
|
+
stays pinned with zero `sticky`/`fixed` and never overlaps content — the
|
|
117
|
+
short-page pitfall of sticky nav bars (below) can't happen here.
|
|
118
|
+
- **Safe-area insets** — the shell pads for landscape notches
|
|
119
|
+
(`inset-left`/`right`), the topbar for `inset-top`, the dock for
|
|
120
|
+
`inset-bottom`.
|
|
121
|
+
|
|
122
|
+
Collapse the rail by toggling `.juno-rail--collapsed` (one class; the width
|
|
123
|
+
transition and label hiding are built in). Trays/detail panels: the
|
|
124
|
+
[slide-over](./components/drawer.md#the-slide-over-pattern) pattern in the
|
|
125
|
+
drawer doc. Knobs: `--juno-app-shell-topbar-size` (topbar height).
|
|
126
|
+
|
|
127
|
+
### Narrow viewports (phone)
|
|
128
|
+
|
|
129
|
+
The rail↔dock swap is two classes, no JS: `.juno-rail--responsive` self-hides
|
|
130
|
+
below `md`, and the dock carries `.juno-hide-from-md` so it shows only there.
|
|
131
|
+
(Equivalent to hanging `.juno-hide-below-md` on the rail yourself — the
|
|
132
|
+
modifier just saves you knowing to.) Keep 3–5 dock destinations; the rest go
|
|
133
|
+
behind a "More" item (drawer or menu). Prefer a floating bar? Swap the dock
|
|
134
|
+
for a [pillbar](./components/pillbar.md) — same contract, capsule look.
|
|
135
|
+
|
|
136
|
+
**Viewport helpers.** For the cases an intrinsic primitive can't express
|
|
137
|
+
(swap a nav for a menu button), hide/show by breakpoint at `sm` (640px),
|
|
138
|
+
`md` (768px), `lg` (1024px):
|
|
139
|
+
|
|
140
|
+
| Class | Visible |
|
|
141
|
+
| -------------------------------------------- | -------------- |
|
|
142
|
+
| `.juno-hide-below-md` / `.juno-show-from-md` | at `md` and up |
|
|
143
|
+
| `.juno-hide-from-md` / `.juno-show-below-md` | below `md` |
|
|
144
|
+
|
|
145
|
+
`show-*` are readable aliases for the inverse `hide-*` (`show-from-md` ≡
|
|
146
|
+
`hide-below-md`); pick whichever reads clearer at the call site. Same three
|
|
147
|
+
cut points for `sm` and `lg`.
|
|
148
|
+
|
|
149
|
+
### Page-scroll shells (dock/pillbar `--fixed`)
|
|
150
|
+
|
|
151
|
+
`.juno-app-shell` is the recommended frame because its `__main` scroller keeps
|
|
152
|
+
the dock in flow. If instead the **whole page** scrolls, the dock/pillbar use
|
|
153
|
+
`position: sticky` — which only pins _while the column overflows_. On a short
|
|
154
|
+
page that doesn't scroll, a sticky bar lands mid-content, reading as "not
|
|
155
|
+
stuck." For that layout use `.juno-dock--fixed` (pins flush to the viewport
|
|
156
|
+
foot) or `.juno-pillbar--fixed` (fixes to the viewport, still floating its gap
|
|
157
|
+
above the foot) — then reserve the bar's footprint at the page foot (e.g.
|
|
158
|
+
`padding-block-end`) so it doesn't cover the last row.
|
|
159
|
+
|
|
160
|
+
### Tab + stack (phone navigation recipe)
|
|
161
|
+
|
|
162
|
+
The full phone pattern is three parts: the dock or pillbar switches
|
|
163
|
+
_sections_; inside a section, a [list](./components/list.md) row (or any
|
|
164
|
+
link) pushes a detail view; every pushed view opens with a
|
|
165
|
+
[navbar](./components/navbar.md) whose back control unwinds one level.
|
|
166
|
+
junoui ships all three looks — the app owns the stack (routing/history):
|
|
167
|
+
|
|
168
|
+
```html
|
|
169
|
+
<div class="juno-app-shell">
|
|
170
|
+
<div class="juno-app-shell__body">
|
|
171
|
+
<!-- one section, drilled one level in -->
|
|
172
|
+
<header class="juno-navbar">
|
|
173
|
+
<a class="juno-navbar__back" href="/settings">
|
|
174
|
+
<svg class="juno-icon" aria-hidden="true"><use href="…#juno-i-caret-left" /></svg>
|
|
175
|
+
<span class="juno-navbar__back-label">Settings</span>
|
|
176
|
+
</a>
|
|
177
|
+
<h1 class="juno-navbar__title">Playback</h1>
|
|
178
|
+
<div class="juno-navbar__actions"></div>
|
|
179
|
+
</header>
|
|
180
|
+
<main class="juno-app-shell__main"><!-- .juno-list groups… --></main>
|
|
181
|
+
<nav class="juno-pillbar" aria-label="Primary"><!-- section tabs --></nav>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Adopting in an existing project
|
|
187
|
+
|
|
188
|
+
No restructuring required — additive and incremental:
|
|
189
|
+
|
|
190
|
+
1. Set `data-juno-palette` / `data-juno-mode`, swap hardcoded values → `var(--juno-*)`.
|
|
191
|
+
2. Add component classes where blocks match.
|
|
192
|
+
3. Wrap layout regions in a primitive (`.juno-grid-auto`, `.juno-cluster`, …) to get
|
|
193
|
+
reflow. Add `container-type: inline-size` to a wrapper for component-level queries.
|
|
194
|
+
|
|
195
|
+
You opt in per region; nothing forces a markup shape.
|
|
196
|
+
|
|
197
|
+
## The JS line
|
|
198
|
+
|
|
199
|
+
These are all CSS. Behavior that needs state (resize observers feeding app state,
|
|
200
|
+
virtualization, drag-resizable panels) belongs in your app or a sibling
|
|
201
|
+
`junoui-<framework>` package — not the design system.
|
package/docs/native.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Native mobile (Android / iOS)
|
|
2
|
+
|
|
3
|
+
Native platforms can't parse `oklch()`, so colors are pre-converted to sRGB hex
|
|
4
|
+
during the build. Values match the web rendering.
|
|
5
|
+
|
|
6
|
+
## Android
|
|
7
|
+
|
|
8
|
+
Copy or sync the generated resources into your module:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
dist/android/colors.xml → res/values/colors.xml
|
|
12
|
+
dist/android/dimens.xml → res/values/dimens.xml
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Reference them:
|
|
16
|
+
|
|
17
|
+
```xml
|
|
18
|
+
<TextView
|
|
19
|
+
android:textColor="@color/standard_dark_nominal"
|
|
20
|
+
android:padding="@dimen/space_16"
|
|
21
|
+
android:textSize="@dimen/font_size_14" />
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- Color names: `<palette>_<mode>_<role>` (e.g. `colorblind_dark_warning`).
|
|
25
|
+
- Dimensions are `dp`; font sizes are `sp`.
|
|
26
|
+
- Theme switching: pick the resource set for the active palette/mode, or split the
|
|
27
|
+
files into qualified `values-night/` for dark mode.
|
|
28
|
+
|
|
29
|
+
## iOS (Swift)
|
|
30
|
+
|
|
31
|
+
Add the generated file to your target:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
dist/ios/JunoTokens.swift
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```swift
|
|
38
|
+
label.textColor = JunoTokens.standardDarkNominal
|
|
39
|
+
let pad: CGFloat = JunoTokens.space16
|
|
40
|
+
let body: CGFloat = JunoTokens.fontSize14
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- Colors are `UIColor` constants named `<palette><Mode><Role>` (camelCase).
|
|
44
|
+
- Dimensions are `CGFloat` constants.
|
|
45
|
+
|
|
46
|
+
## Keeping in sync
|
|
47
|
+
|
|
48
|
+
Re-run `npm run build` in junoui and re-copy the files (or script the copy in your
|
|
49
|
+
CI). Because all platforms derive from the same `tokens/` source, the values can
|
|
50
|
+
never drift between web and native.
|
|
51
|
+
|
|
52
|
+
Exact values for every token: [tokens-reference.md](./tokens-reference.md).
|