From 73c72cedaad6cae2b55c037b47b8fb1c794b4e72 Mon Sep 17 00:00:00 2001 From: Babissimo Date: Tue, 15 Sep 2026 12:54:10 +0100 Subject: [PATCH] docs(brand): record data.retina.fm, and the theme system three surfaces share MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guide described five surfaces and one themed one. Both are now out of date: data.retina.fm has existed for a while and was never captured, and retina-server #382 and #383 gave dash, admin and data a dark theme built on the map's palette rather than on sets of their own. Read cold today, the guide would have someone invent a sixth palette for a surface that already has one. So: data joins the tables, and the pair of palettes is documented as a pair rather than as the map's private business. §3 also gains the two tokens the sharing introduced, `--bg-sunk` and `--accent-ink`. The part worth getting right is that the three do not answer the same question the same way. dash and data have three states and default to `system`, which stamps no attribute and lets a `prefers-color-scheme` block answer, so the OS preference keeps working when it changes mid-session; the map has two and defaults to dark, never consulting the OS. What they do share is the cascade rule — a surface puts the half it must paint before JavaScript on the bare selector and spends `data-theme` on the other — and that is a separate claim from which half a viewer meets. §10 records the split rather than resolving it. `preview.html` said "the console in light" and "the console in dark" of its dash and map panels, which reads as two surfaces with different looks. They are now marked as the two halves of one palette: a shared accent rail, a kicker naming the half, and a note in each saying which surfaces arrive there and how. Where dash and data hold identical values they share a column, which keeps the tables the width they were. The component matrix is the exception, since that is precisely where the two differ: data has the shared control rule, the focus treatment and the styled overlay that §10 has been asking dash for, so those are recorded as the versions to copy. Its top bar is recorded but explicitly not proposed for dash, which has two navigation trees the bar cannot hold. Three corrections found while checking claims against the stylesheets: - §2 said the console sets its micro-label in mono. dash never has; its stat labels, table headers and nav-section titles are all the system stack, and data copied that. Only the two marketing sites use mono here. - tokens.css had the map at radius 6/3/10 against the 8/4/12 the surface actually uses and §5 already records. - dash and data set `--bg-sunk` to the canvas value while the map's light theme sets it one step lighter, which is the single place the shared pair disagrees. Recorded in §10 rather than resolved, per this file's policy. Co-Authored-By: Claude Opus 5 --- docs/brand/brand-guide.md | 386 +++++++++++++++++++++++++------------- docs/brand/preview.html | 42 ++++- docs/brand/tokens.css | 77 ++++++-- 3 files changed, 357 insertions(+), 148 deletions(-) diff --git a/docs/brand/brand-guide.md b/docs/brand/brand-guide.md index 71f5cb4..fe7da0a 100644 --- a/docs/brand/brand-guide.md +++ b/docs/brand/brand-guide.md @@ -9,19 +9,27 @@ > Companion file: [`tokens.css`](tokens.css) holds the same values as CSS custom > properties, so a page can consume them instead of transcribing hex codes. -The design lives in five hand-authored surfaces, none of which shares a +The design lives in six hand-authored surfaces, none of which shares a stylesheet with the others: - **offworldlabs.com** ([`landing-page-owl`](https://github.com/offworldlabs/landing-page-owl)) — the lab - **retina.fm** ([`landing-page-retina`](https://github.com/offworldlabs/landing-page-retina)) — the product - **dash.retina.fm** (`retina-server/dashboard`) — the admin console +- **data.retina.fm** (`retina-server/data-explorer`) — the public archive browser - **map.retina.fm** (`retina-server/frontend`) — the live radar map - **owl.local** ([`retina-gui`](https://github.com/offworldlabs/retina-gui)) — the node's own UI They read as one family, but each has its own voice. Two sit apart: the map is -the only dark surface, a Flightradar24-style operational console, and the only -one that themes, offering light alongside its default dark (§3); owl-os is the -only one that runs on hardware in someone's house rather than on a server. +the only one dark by default, a Flightradar24-style operational console; owl-os +is the only one that runs on hardware in someone's house rather than on a +server. + +**Three of them theme, on one palette.** The map has offered light beside its +default dark for longest; dash and data now carry both halves of that same pair +rather than inventing a ramp of their own, and follow the OS unless told +otherwise (§3). The other three are light only. `admin.retina.fm` is dash's own +bundle behind a role check rather than a seventh surface, so it themes with it +and is not listed separately anywhere below. owl-os is the odd repository as well as the odd surface. The UI is authored in [`retina-gui`](https://github.com/offworldlabs/retina-gui) but shipped by @@ -50,32 +58,35 @@ blah2 rather than to any surface described here. --- -## 1. Brand architecture: one family, five voices +## 1. Brand architecture: one family, six voices The surfaces share a spine (a blue accent, green/amber status colours, a small uppercase micro-label, hairline rules) and then diverge in personality. The split -maps onto audience. Four are light; the map is dark by default and can be -switched to light. - -| | **owl** — offworldlabs.com | **retina** — retina.fm | **dash** — dash.retina.fm | **map** — map.retina.fm | **owl-os** — owl.local | -|---|---|---|---|---|---| -| **Role** | The lab / parent org | The product you buy | The console you operate | The live picture | The node you own | -| **Audience** | Researchers, funders, press | Prospective node owners | Logged-in operators | Anyone watching the map | Whoever has the box | -| **Feel** | Editorial, institutional | Commercial, confident | Utilitarian, dense | Dark ops console (FR24-style) | Appliance settings, unhurried | -| **Serif** | Source Serif 4, wt 400 | Fraunces, wt 600 | none | none | none | -| **Sans** | Inter | DM Sans | system stack | Inter (only font) | Inter | -| **Mono** | IBM Plex Mono | JetBrains Mono | SF Mono | none (generic) | JetBrains Mono | -| **Canvas** | warm paper `#f7f6f2` | warm paper `#fafaf9` | cool slate `#f1f5f9` | **navy `#0d1b2a`**, light `#f1f5f9` | near-white warm `#fffdfb` | -| **Blue** | muted `#5b8dd9` | punchy `#2563eb` | `#3b82f6` → `#2563eb` | sky `#38bdf8`, light `#3b82f6` | `#2f8adc`, accents only | -| **Radius** | 4–6px | 8–20px | 4–8px | 3–10px | 6–14px | -| **Buttons** | flat | lift + shadow | flat | flat | flat, primary is ink | -| **Dark UI** | none | one dark section | none | **dark by default**, light opt-in | none | +maps onto audience. Three of the six are light and nothing else; the other +three carry both halves of one palette pair, and differ in what decides which +half you get (§3). + +| | **owl** — offworldlabs.com | **retina** — retina.fm | **dash** — dash.retina.fm | **data** — data.retina.fm | **map** — map.retina.fm | **owl-os** — owl.local | +|---|---|---|---|---|---|---| +| **Role** | The lab / parent org | The product you buy | The console you operate | The archive you fetch from | The live picture | The node you own | +| **Audience** | Researchers, funders, press | Prospective node owners | Logged-in operators | Anyone wanting the data | Anyone watching the map | Whoever has the box | +| **Feel** | Editorial, institutional | Commercial, confident | Utilitarian, dense | Utilitarian, dense | Dark ops console (FR24-style) | Appliance settings, unhurried | +| **Serif** | Source Serif 4, wt 400 | Fraunces, wt 600 | none | none | none | none | +| **Sans** | Inter | DM Sans | system stack | system stack | Inter (only font) | Inter | +| **Mono** | IBM Plex Mono | JetBrains Mono | SF Mono | SF Mono | none (generic) | JetBrains Mono | +| **Canvas** | warm paper `#f7f6f2` | warm paper `#fafaf9` | cool slate `#f1f5f9`, dark `#0d1b2a` | cool slate `#f1f5f9`, dark `#0d1b2a` | **navy `#0d1b2a`**, light `#f1f5f9` | near-white warm `#fffdfb` | +| **Blue** | muted `#5b8dd9` | punchy `#2563eb` | `#3b82f6` → `#2563eb`, dark `#38bdf8` | `#3b82f6` → `#2563eb`, dark `#38bdf8` | sky `#38bdf8`, light `#3b82f6` | `#2f8adc`, accents only | +| **Radius** | 4–6px | 8–20px | 4–8px | 4–8px | 2–12px | 6–14px | +| **Buttons** | flat | lift + shadow | flat | flat | flat | flat, primary is ink | +| **Dark UI** | none | one dark section | **follows the OS**, the map's palette | **follows the OS**, the map's palette | **dark by default**, light opt-in | none | A rough rule of thumb for a new page: **who is it for?** Reaching outward to the scientific or funding world reads as owl. Selling or explaining the kit reads as -retina. Anything behind a login reads as dash. Anything that _is_ the live radar -picture (a map, a track view, an operational overlay) reads as map. Anything a -node owner does to their own hardware reads as owl-os. +retina. Anything behind a login reads as dash. Anything that hands the public +the archive reads as data, which is dash's vocabulary without the login and +without the sidebar. Anything that _is_ the live radar picture (a map, a track +view, an operational overlay) reads as map. Anything a node owner does to their +own hardware reads as owl-os. dash and owl-os are worth telling apart, since both are settings pages for the same hardware. dash is an operator holding a fleet at arm's length: dense, @@ -87,16 +98,15 @@ column, one decision per row, and explains each in a sentence underneath. ## 2. Foundations -The parts that stay roughly constant across all five, and are the safest thing +The parts that stay roughly constant across all six, and are the safest thing to carry into new work. -- **A paper-like canvas.** Four of the five use a near-white background (warm on - the marketing sites and owl-os, cool slate on the console) with near-black - text; owl-os pushes furthest toward white at `#fffdfb`. The **map is the - exception**: dark navy with light slate text, and the only surface that - themes, since it carries the console's light palette as an option too (§3). So - "light paper" is the norm for anything that explains, sells or configures, and - dark is the default for the live operational view. +- **A paper-like canvas.** Three of the six are near-white and only near-white + (warm on the marketing sites, `#fffdfb` on owl-os, which pushes furthest + toward white). dash and data are cool slate on a light OS and navy on a dark + one; the **map is navy** whatever the OS says. So "light paper" is the norm + for anything that explains or sells, and the three surfaces that operate the + network are the three that can go dark (§3). - **A blue accent.** owl sits soft and desaturated, retina and dash land on the brighter `#2563eb`/`#3b82f6`, owl-os lands between them at `#2f8adc`, and the map goes brighter still (sky `#38bdf8`) to carry on dark, dropping back to @@ -105,17 +115,20 @@ to carry into new work. `#fbbf24` as a selection/focus highlight.) owl-os spends its blue the most sparingly: the primary button is ink, and the accent is kept for focus rings, hover borders, selection washes and the one card that is _this_ node. + data spends dash's blue on light and the map's on dark, adding none of its own. - **Green and amber as status colours.** Green marks "live" / validated / ADS-B - truth; amber marks the radar echo, anomalies, and attention. The console adds + truth; amber marks the radar echo, anomalies, and attention. The consoles add red for outright errors. (The exact shades drift between surfaces; see §10.) - **A small uppercase micro-label.** Small, uppercase, letter-spaced, muted. Used for section eyebrows, stat labels, table headers, nav-section titles, and diagram annotations. This is the single most reused idiom and the quickest way - to make a new page read as "ours". All five surfaces have it. The marketing - sites and console set it in their mono; the map and owl-os keep the - uppercase-and-tracked treatment for section and panel headers in their sans, - the map because it has no mono and owl-os by choice (it has JetBrains Mono and - spends it on data instead). + to make a new page read as "ours". All six surfaces have it, and only the two + marketing sites set it in their mono. dash, data, the map and owl-os keep the + uppercase-and-tracked treatment in their sans: the two consoles because dash + never spent its mono on it (stat labels, table headers and nav-section titles + are all the system stack, and data copied that), the map because it has no + mono, and owl-os by choice, having JetBrains Mono and spending it on data + instead. - **Hairline rules and grid dividers.** 1px borders at low contrast separate sections, table rows, fact lists, and card grids. Structure comes from lines, not shadows or fills. @@ -139,10 +152,10 @@ roles (semantics) are shared unless a surface overrides them. ### Canvas and ink -| Role | owl | retina | dash | map | owl-os | +| Role | owl | retina | dash / data | map | owl-os | |---|---|---|---|---|---| | Canvas | `#f7f6f2` | `#fafaf9` | `#f1f5f9` | `#0d1b2a` | `#fffdfb` | -| Sunk surface | `#edecea` | `#f5f4f0` | `#f8fafc` | `#0f2035` | `#fbfaf8` | +| Sunk surface | `#edecea` | `#f5f4f0` | `#f1f5f9` (= canvas) | `#0f2035` | `#fbfaf8` | | Card / panel | `#ffffff` | `#ffffff` | `#ffffff` | `#132240` | `#ffffff` | | Dark section | — | `#090904` / `#242422` | — | (the dark theme) | — | | Ink (primary) | `#0e0e0c` | `#1a1a18` | `#0f172a` | `#e2e8f0` | `#13161c` | @@ -150,25 +163,55 @@ roles (semantics) are shared unless a surface overrides them. | Ink (subtle) | `#888882` | `#9c9c93` | `#94a3b8` | `#64748b` | `#83868c` | | Border | `rgba(14,14,12,.1)` | `#e8e8e3` | `#e2e8f0` | `rgba(100,180,255,.14)` | `#eae7e4` | -The marketing canvases are warm (a hint of yellow); the console is cool slate; +**dash and data share a column because they share every value in it.** data was +built by copying dash's token block, and the two have not diverged; where the +rest of this guide says "dash" about a colour, it is true of data as well. + +The marketing canvases are warm (a hint of yellow); the consoles are cool slate; the map inverts to dark navy. The map's ink ramp is the dark mirror of the console's slate ramp (`#94a3b8` / `#64748b` recur), and its hairlines are a blue-tinted translucent white rather than a solid grey. -**The map's column above is its dark theme, which is the default.** Its light -theme takes dash's values wholesale: canvas `#f1f5f9`, sunk `#f8fafc`, card -`#ffffff`, the slate ink ramp, border `#e2e8f0`. The switch is a `data-theme` -attribute on the surface root, and the base selector carries dark while the -attribute carries light, so the default cannot flash the other theme before the -attribute is stamped. Everything on the surface is written against the custom -properties, so the whole chrome inverts from that one block. - -The sunk tier is the one place the two are not mirror images, and the reason the -map needs a token the light surfaces can do without. On a light surface a -recessed region is made by letting the canvas show through, the canvas already -being darker than a card; the map's ramp inverts, so a recessed pane needs a -colour of its own. It pays for the left aircraft list and, on the Physics tab, -the scene, ground-truth and solver sections. +**The map's column above is its dark theme, which is the default there.** These +are not two palettes but one pair, and three surfaces now run it. The map's +light theme takes dash's values almost wholesale (§10 has the one exception), +and dash and data carry the same pair with the halves the other way up. + +**What decides which half you get is not the same question on the two**, and it +is the part most easily got wrong. dash and data **follow the OS**: their +control has three states, and the third, `system`, is the default and the one a +viewer who has never touched the control is on. It stamps no attribute at all +and lets a `prefers-color-scheme` block answer, so the preference keeps working +when the OS changes its mind mid-session — where stamping a resolved value +would pin the surface to whatever the OS happened to be at load. The map +**never asks the OS**: its control is a boolean, and dark is what a first visit +gets on a machine set to light. + +What all three do share is the cascade rule. A surface puts on its bare selector +the half it must be able to paint before any JavaScript runs, and spends +`data-theme` on the other: dark on the map, light on dash and data. The +attribute is stamped from JavaScript, so whichever half depends on it is the one +that can flash the other before first paint. On dash and data the media query is +guarded `:not([data-theme="light"])`, which is what lets an explicit light +choice beat a dark OS. Everything on all three is written against the custom +properties, so the whole chrome inverts from one block. + +Two tokens exist because of that sharing. + +**`--bg-sunk` is the third surface tier**, and all three themed surfaces carry +it. On a light surface a recessed region can be made by letting the canvas show +through, the canvas already being darker than a card; the dark ramp inverts, +card lighter than canvas, so a recessed pane needs a colour of its own at +`#0f2035`. It pays for the map's left aircraft list and, on the Physics tab, the +scene, ground-truth and solver sections, and for data's appearance switch. The +light half of it is the one value the three do not agree on (§10). + +**`--accent-ink` is what sits _on_ the accent** rather than beside it, and is +dash's and data's alone — the map defines none. It is a token and not a literal +white because the dark accent is a bright sky blue, on which white is about +1.8:1; there it becomes a near-black `#082f49`. Any solid accent fill carrying +text — a primary button, the sidebar mark — needs it, so a component moved onto +the map has to bring its own answer. owl-os crosses the two: a warm canvas like the marketing sites, over a **cool** ink ramp like the console. It is also the only surface authored in **OKLCH** @@ -183,7 +226,7 @@ thirty stacked rows from reading as a grid. ### Accent -| | owl | retina | dash | map | owl-os | +| | owl | retina | dash / data | map | owl-os | |---|---|---|---|---|---| | Accent | `#5b8dd9` | `#2563eb` | `#3b82f6` | `#38bdf8` | `#2f8adc` | | Hover / strong | `#4a7cc8` | `#3b82f6` | `#2563eb` | `#7dd3fc` | `#1a7acb` | @@ -195,8 +238,10 @@ owl's blue is deliberately soft and low-contrast (an editorial choice, though it costs link legibility, see §10). retina and dash share the saturated blue, with dash resting one step lighter and hovering to retina's resting value. The map pushes to a brighter sky-blue so the accent reads on navy, and takes dash's -value when themed light rather than adding a sixth. owl-os sits between dash and -the map, a shade cyan-ward of both. Five distinct blues in all. +value when themed light rather than adding another. owl-os sits between dash and +the map, a shade cyan-ward of both. Still five distinct blues across six +surfaces: dash and data are one value, and the themed surfaces swap between two +that already exist rather than introducing a third. The map is the only surface with **two** accent washes. The tint is the wash at hover strength and sits deliberately below the wash proper, so a hovered row and @@ -213,16 +258,18 @@ region is bounded rather than bleeding into the page. The map adds an amber ### Semantics -| Role | Marketing (owl, retina) | Console (dash) | Map (map) | Node (owl-os) | +| Role | Marketing (owl, retina) | Consoles (dash, data) | Map (map) | Node (owl-os) | |---|---|---|---|---| | Success / good | `#16a34a` | `#10b981` (emerald) | `#4ade80` | `#33a868` | | Warning / attention | `#d97706` | `#f59e0b` (amber-500) | `#fbbf24` | `#e1a035` | | Error | (unused) | `#ef4444` (red-500) | `#f43f5e` | `#e64343` | Same three roles, brightened a step on the dark map so they carry on navy, and -landing between the marketing and console sets on owl-os. The map's light theme -takes the console's set unchanged. Each pairs with a wash (~10% on light, ~15% -on dark; opaque on owl-os) for tinted pill backgrounds. The console's charts +landing between the marketing and console sets on owl-os. The two sets travel +with the theme rather than with the surface: the map's light theme takes the +console set unchanged, and dash's and data's dark takes the map's. Each pairs +with a wash (~10% on light, ~15% on dark; opaque on owl-os) for tinted pill +backgrounds. The console's charts extend the accent into a categorical palette (`#3b82f6, #10b981, #f59e0b, #ef4444, #8b5cf6, #ec4899, #06b6d4, #84cc16, #f97316, #14b8a6`, with `#94a3b8` for an "others" slice); reuse that ordering for any new @@ -267,27 +314,27 @@ face for running text and UI, and a **mono** face for labels and data. The console collapses everything into the system stack; the map collapses everything into Inter. -| Register | owl | retina | dash | map | owl-os | +| Register | owl | retina | dash / data | map | owl-os | |---|---|---|---|---|---| | Display | Source Serif 4 | Fraunces | system sans | Inter | Inter | | Body | Inter | DM Sans | system sans | Inter | Inter | | Mono | IBM Plex Mono | JetBrains Mono | SF Mono | generic `monospace` | JetBrains Mono | Loaded from Google Fonts on the marketing sites, the map (Inter) and owl-os; the -console loads no web fonts at all (its CSP restricts `font-src`). Only the two -marketing sites use a serif; the three operational surfaces (dash, map, owl-os) -are sans-only. +two consoles load no web fonts at all (their CSP restricts `font-src`). Only the +two marketing sites use a serif; the four operational surfaces (dash, data, map, +owl-os) are sans-only. owl-os is the only surface that borrows both its faces from siblings rather than choosing new ones (Inter from owl and the map, JetBrains Mono from retina), and -so is the least type-divergent of the five. It also asks Inter for +so is the least type-divergent of the six. It also asks Inter for `font-feature-settings: 'ss01', 'cv11'` (the single-storey `g` and the straight-tailed `l`), which is what stops a screen full of node IDs and frequencies from reading as body copy. **Heading weight is the other big tell.** owl sets headings at 400 (light, -serious, editorial); retina and owl-os at 600 (present, product-confident); dash -and map at 700 (compact, functional). Marketing headings carry a tight +serious, editorial); retina and owl-os at 600 (present, product-confident); dash, +data and map at 700 (compact, functional). Marketing headings carry a tight `letter-spacing` around `-0.02em` and line-height near 1.1; the operational surfaces run text small (down to ~0.62rem) and dense. @@ -319,21 +366,23 @@ settings, large enough not to read as a table. ### Shape -| | owl | retina | dash | map | owl-os | +| | owl | retina | dash / data | map | owl-os | |---|---|---|---|---|---| | Radius (default) | 4px | 12px | 8px | 8px | 10px | | Radius (small) | 3px | 8px | 4px | 4px | 6px | | Radius (large) | 6px | 20px | 12px | 12px | 14px | -Crisp on owl, soft on retina, moderate on dash and on the map, which take the -same scale, second-softest on owl-os. Borders are always 1px hairlines; nothing -uses a heavy stroke. The map's panels float over a moving basemap rather than -sitting on a page, so it is the one surface where a card carries a shadow as +Crisp on owl, soft on retina, moderate on dash, data and the map, which take the +same scale, second-softest on owl-os. data defines only the first two steps, +having nothing large enough to need the third. Borders are always 1px +hairlines; nothing uses a heavy stroke. The map's panels float over a moving +basemap rather than sitting on a page, so it is the one surface where a card +carries a shadow as standard (`--panel-shadow`), heavier on dark than on light because a soft shadow does almost nothing there and the elevation has to come from the panel being lighter than the canvas. -The three-step scale is a fair summary for four of the surfaces and a +The three-step scale is a fair summary for five of the surfaces and a simplification for one. The map is now the closest to keeping to it: most of its radius rules read `--radius` or `--radius-sm`, with 12px reserved for pills and a scatter of 2–6px literals left over. retina is the outlier, spending 5, 6, 8, 10, @@ -348,15 +397,17 @@ black, so a raised card warms rather than greys. ### Space - Marketing content sits in a centred column: `max-width` 860px for prose, - 1120–1200px for wide/hero rows. The console is full-width and fluid with a - 250px sidebar. The map is full-bleed: a 280px left aircraft list (collapsing - to 36px), a 280px right detail panel, and a full-width bottom playback bar. + 1120–1200px for wide/hero rows. dash is full-width and fluid with a 250px + sidebar; data has no sidebar to hold (one page, no nav tree, no login) and + runs a 1400px centred column under a top bar instead. The map is full-bleed: + a 280px left aircraft list (collapsing to 36px), a 280px right detail panel, + and a full-width bottom playback bar. - owl-os runs a 720px column on Home, a 200px + fluid two-column shell on Config (max 1100px), and a fixed 560px column for every wizard step. One decision per row, with its explanation directly underneath. -- Nav / header height: 56px (owl), 64px (retina), 56px (dash). owl-os stacks two - rows instead, a fleet bar over the page tabs, and its config side-nav sticks - below both at 58px. +- Nav / header height: 56px (owl), 64px (retina), 56px (dash and data). owl-os + stacks two rows instead, a fleet bar over the page tabs, and its config + side-nav sticks below both at 58px. - Section padding: ~5–6rem vertical on the marketing sites, 24px content padding in the console. - Cards/panels: ~20px body padding, 16px header; grids gap ~16px. @@ -387,27 +438,27 @@ own; the two marketing home pages still run their loops unguarded. The ## 6. Components -The five surfaces do not have the same component set, and the differences are +The six surfaces do not have the same component set, and the differences are larger than the colour and type differences in §3 and §4. Before reaching for a pattern, check it exists on the surface you are building for. -| | owl | retina | dash | map | owl-os | -|---|---|---|---|---|---| -| Form controls | **none** | **none** | inline, ad hoc | one shared rule | a real set | -| Tables | none | none | yes | none | one | -| Modal / dialog | none | none | **native `confirm()`** | one | three | -| Toasts | none | none | none | yes | none | -| Tabs | none | none | one page | app shell only | yes | -| Icons | 36px, part-filled | Unicode glyphs | 24px, stroke 2 | inline SVG | 24px, stroke 1.6 | -| Framework | none | none | none | none | Bootstrap 5.3 | -| Custom properties | yes | yes | yes | yes, themed | yes | -| `:focus` styling | none | none | **none** | whole surface | inputs only | +| | owl | retina | dash | data | map | owl-os | +|---|---|---|---|---|---|---| +| Form controls | **none** | **none** | inline, ad hoc | one shared rule | one shared rule | a real set | +| Tables | none | none | yes | a grid listing, one table | none | one | +| Modal / dialog | none | none | **native `confirm()`** | one drawer | one | three | +| Toasts | none | none | none | none | yes | none | +| Tabs | none | none | one page | none | app shell only | yes | +| Icons | 36px, part-filled | Unicode glyphs | 24px, stroke 2 | 15px inline SVG | inline SVG | 24px, stroke 1.6 | +| Framework | none | none | none | none | none | Bootstrap 5.3 | +| Custom properties | yes | yes | yes, themed | yes, themed | yes, themed | yes | +| `:focus` styling | none | none | **none** | inputs | whole surface | inputs only | The two marketing sites have no `
`, ``, `