Tier 2 · UI design
UI design: what a screen should look and read like
Hierarchy, contrast, copy, motion, and the rule that text never resizes its shell.
Hierarchy, contrast, copy, motion, and the rule that text never resizes its shell.
UI design — what a screen should look and read like
What the screen should look and read like when the agent is the one making the visual decisions, and what a design review checks. How UI Toolkit behaves is the UI Toolkit runtime page. How to port a reference is the HTML/CSS mock to UI Toolkit page. What USS lacks is the uss uxml page. Cross-framework shell geometry is the UI design page.
Status (OWNER-VOICE). An agent extracted the rules below from
outside sources. Each one is agent default — unconfirmed until the developer ratifies
it. No rule here may be marked HARD or enforced as law until a ledger row cites the developer
directive behind it. Confirm or reject each heading on its own.
License credit. The design sources are rewritten here for Unity UI Toolkit (USS/UXML), not copied. Keep this attribution. The motion-verification tag is attribution only, not a license.
- = Impeccable (pbakaus/impeccable @ d631a88, skill), Apache License 2.0.
is a rule id in Impeccable's deterministic detector
(
crates/foundation/src/registry.rs). [AS: id]= antislop (miqdadbadjuber/anti-slop @ 388cbe3, v3.2.20), MIT License.[FLX: note]identifies motion-process notes inherited from the source material; the original post URL could not be verified. These notes are project defaults, not incident evidence.
Re-diff occasionally against the Impeccable and antislop skill references these rules came from.
Contains only net-new rules. Where a rule touches an existing rule, it names that rule and its file and does not restate it. A coverage pass mapped 436 source items: 180 in this draft, 73 already covered by other spokes, 63 excluded, and 120 added.
Precedence
UI-DESIGN-BRIEF-WINS (agent default — unconfirmed)
- Developer reference or brief > the HTML/CSS mock to UI Toolkit page PORT-FROM-THE-RENDERED-REF > a developer-approved direction (UI-APPROVED-DIRECTION-IS-RECORDED) > this file. A ported design keeps its glow, grid, display face, arcade bottom-border shadow, tracked caps and idle motion even when a rule below would flag them.
- This file decides the choices a reference leaves open, and it is what a review measures an agent-originated screen against.
- Why: both sources fail open in the same way. Without precedence, a taste rule overrides a ratified design.
[AS: R-37]
Direction
UI-DIRECTION-SOURCE-ORDER (agent default — unconfirmed)
Before building a screen, take the visual direction (identity, palette, type, mood, density) from the first source that exists: (1) a developer reference or brief, (2) the direction recorded in the project's pillar or project explanation documents, (3) the established look of the surrounding screens (extend it, never re-invent it for one new screen). If none exists, write a full-quality proposal (UI-PROPOSED-DIRECTION) instead of silently falling back to a neutral default.
[AS: R-37]
UI-PROPOSED-DIRECTION (agent default — unconfirmed)
With no direction on record, the agent may author one. A proposal is complete work, not a sketch:
- a Design Read line: "Reading this as:
<screen>for<player>, in<visual language>, mode<UI-SURFACE-MODE>", plus antislop's ENERGY / RHYTHM / MOTION dials (1 calm, 2 balanced, 3 bold) chosen by the proposal, not locked at 1; - a direction card: the world it draws from (the game's own fiction, setting and genre
objects, not UI trends), palette as
:roottokens with roles, type roles with the actual TextCore FontAssets, spacing scale, radius and elevation tokens, the one signature move, a motion thesis, and an honest risk line; - at least one fully built draft screen in UXML/USS/C# at production quality: real content authored at full fidelity, every state from UI-CONTROL-STATE-SET and UI-STATES-NAME-CAUSE-AND-NEXT-ACTION, contrast numbers met, and the pre-ship checklist run with its evidence class reported;
- a label: "PROPOSAL, awaiting developer approval" in the plan or ledger row, the handoff, and the header of the proposal's token sheet. The label stays out of player-visible text unless the developer wants a visible watermark.
Before choosing, check the candidate against the AI-default looks (warm cream ground + high-contrast serif + terracotta accent; near-black + one neon accent + glowing edges; editorial hairlines + italic serif + tracked mono labels; flat saturated ink + black boxes + hard offset shadows + heavy condensed display) and treat the first look that comes to mind as spent. Logos, brand marks and real-person likenesses in a proposal are proposals too, and are labelled as such.
Not adopted: antislop's forced ENERGY 1 / RHYTHM 1 / MOTION 1 lock and "not a shippable
deliverable" framing (which caps the proposal at a deliberately flat result), [REAL DATA] /
[LOGO] stub markers, and Impeccable's web tooling (concept-seed dice, decision page,
image-comp build phases). The stubs produce placeholder-quality output; the tooling does
not run on UI Toolkit.
[AS: R-37 + install-wizard step 3 options 2/3, Part 3 Design Read and dials]
UI-PROPOSAL-ALTERNATES (agent default — unconfirmed)
When the direction is genuinely open (a new game or a replacement look, not one screen inside an established one), present one fully committed lead direction and up to two alternates. Each alternate is complete enough to judge on its own (direction card plus a built key screen, or the same screen re-skinned through the token sheet). Optionally add the genre-standard look played straight as a quiet alternative. Never pad the choice with thin variants that only their description can tell apart. USS makes this cheap: alternates share the UXML and differ by a root theme class that redefines the tokens (the retheme pattern in the uss uxml page).
UI-APPROVED-DIRECTION-IS-RECORDED (agent default — unconfirmed)
When the developer approves (or edits) a proposal, record the decision in one line in the ledger, and write the durable direction (tokens, type roles, signature move, motion thesis, dials) into the project's pillar docs, or the design doc they route to. Write it from the built screens, not from the pre-build card, so it describes reality. A rejected proposal's token sheet and screens are removed or parked in the same change, so two looks never live in the shipped path (PLACEHOLDER-DIES-WITH-THE-SHAPE in the system blueprint page).
[AS: R-37 "record the answer in one line"]
UI-SURFACE-MODE (agent default — unconfirmed)
Name each screen's mode in its plan or ledger row. The mode sets density, type contrast and motion budget.
| Mode | Game screens | Defaults |
|---|---|---|
| Operate | HUD, inventory, crafting, settings, lobby, shop list, Designer Console tabs | restrained colour, one text family, tight type ratio, 150-250 ms motion, consistent controls |
| Experience | title, splash, victory/defeat beats, reveal moments | the art leads, display type may carry the voice, one authored motion moment |
| Persuade | store offer, premium/bundle upsell | one decision, one primary action, honest numbers only |
| Read | codex/lore, patch notes, tutorial text | measured line length, bounded scroll, calm |
UI-REASON-PER-DECISION (agent default — unconfirmed)
Write one line per major visual decision (palette, type roles, spacing scale, container choice, motion) in the plan or ledger row, never in the chat ending. If the reason does not fit on one line, or is "it's the default", change the decision.
[AS: R-31, C-1]
UI-SWAP-THE-LOGO-TEST (agent default — unconfirmed)
With the game's name and logo covered, would the screen still read as this game? If not, it is missing an identity motif (one repeated shape, gesture or type voice from the direction). Add that motif. Do not answer with more decoration.
[AS: Core Principle, R-20, Part 3 identity motif]
UI-DONT-CLONE-UNASKED (agent default — unconfirmed)
Do not reproduce another game's or product's UI language unless the developer names it as the reference.
[AS: R-30]
Hierarchy and layout
UI-ONE-FOCAL-POINT (agent default — unconfirmed)
Each screen has exactly one element that is clearly most important, and at most one primary action. Everything else is secondary or tertiary.
[AS: Part 3 levers]
UI-SQUINT-TEST (agent default — unconfirmed)
Verification: downscale a target-resolution capture to about 1/8 (or blur it) and confirm the focal element, the secondary element and the major groups are still identifiable in order. Run it on the same capture the pre-ship sweep already takes.
UI-SPACING-SCALE (agent default — unconfirmed)
Spacing comes from one token scale on :root (4-unit base: --space-1: 4px up to about
--space-8), plus the half and negative-half tokens that USS-HAS-NO-SIBLING-OR-CALC
(the uss uxml page) needs, because there is no gap or calc. Use tight intervals
inside a group and generous ones between groups, with more space above a heading than
below it. Do not use one-off px values.
[AS: Uniform Spacing]
UI-PROXIMITY-BEFORE-CONTAINERS (agent default — unconfirmed)
Group by space before adding a panel, border or plate. Do not nest painted panels (a card inside a card). Cards are for equivalent, actionable items such as shop entries, not a default wrapper. USS cost note: every extra painted plate is another 9-slice and possibly another unatlased bind.
[AS: R-14]
UI-SCREEN-FROM-THE-DECISION (agent default — unconfirmed)
Build management and stat screens around the decision the player makes there. Do not reach for the template shell (side rail + four stat cards + chart + table) or the hero-metric block (big number, small label, supporting stats). Use identical card grids only when the items really are equivalent.
[AS: antislop-ui "Default Dashboard Shell", R-05]
UI-MODAL-IS-NOT-THE-DEFAULT (agent default — unconfirmed)
Use a modal only for a destructive or irreversible confirm, or when focus must be protected. Information goes inline or in a tooltip. Existing scrim mechanics still apply.
UI-DECISION-LOAD (agent default — unconfirmed)
Check every decision point for the eight cognitive-load failures: a wall of options (more than about 4 equal-weight choices with no grouping or recommended pick), a memory bridge (the player must remember a value from another screen to choose here, such as an item's stats while in the shop), hidden location (no visible "you are here" in tabbed or nested menus), unexplained jargon, a flat noise floor (everything the same weight), the same action working differently on two screens, several simultaneous demands (read, decide and navigate at once), and a context switch (jumping between tabs to gather facts for one choice). Fix by grouping, co-locating the compare data (side-by-side current vs new stats), and keeping the active tab or breadcrumb lit.
UI-CHARTS-AND-TABLES-ANSWER-A-QUESTION (agent default — unconfirmed)
A chart (post-match graph, economy history, damage breakdown) is drawn only when it answers a named question, and the title states that question ("Damage taken per wave"). If a sentence answers it better, write the sentence. List and table columns (leaderboard, inventory list, server browser) come from the decision the player makes there, with the deciding field early. Do not use the generic Name / Status / Date / Actions set, and a row menu holds only actions that exist. Chart segments meet the 3:1 non-text contrast.
[AS: antislop-ui "Charts Without a Question", "Generic Table Columns"]
UI-NAVIGABLE-WITHOUT-POINTER (agent default — unconfirmed)
On any platform with keyboard or gamepad input, every screen can be fully operated without
a pointer. Focus order follows visual order (set tabIndex and the navigation neighbours
deliberately; never leave focus to land on hidden or display: none controls).
Cancel/back (Escape, B/Circle) closes the top overlay and returns focus to the control
that opened it. Drag interactions (inventory, loadout) have a select-then-place
alternative. Menus that open on hover also open on focus or submit. Mechanics live in
the uss uxml page (focusable, tabIndex) and the UI Toolkit runtime page
(NavigationMoveEvent); this rule is the design requirement they serve.
[AS: R-32, antislop-human "Mouse-Only Patterns", "Broken Tab Order"]
Type
UI-TYPE-ROLES (agent default — unconfirmed)
Name the roles (display, title, body, label, meta, number) as tokens
(--type-title-size). An Operate screen uses at most 3-4 sizes and 2-3 weights. Adjacent
roles differ by a visible step: a ratio of about 1.125-1.2 on Operate screens, larger on
Experience screens, and at least one step (title over body) is ≥ 1.25x so the hierarchy
is never flat. Display size is for short strings (one to three words, a hero number). A
full sentence set at display size is the oversized-headline tell: give it the title role.
USS notes: there is no numeric font-weight, so a weight step means a separate TextCore
FontAsset per cut (VARIABLE-FONT-DEFAULTS-TO-ITS-CUT in the HTML/CSS mock to UI Toolkit page), never
-unity-font-style: bold as a stand-in.
UI-DISPLAY-FACE-STAYS-IN-DISPLAY (agent default — unconfirmed)
On Operate screens the display face is for titles and hero numbers. Labels, buttons and data use the text face. Experience screens may let the display face lead. Softened for games.
UI-PROSE-MEASURE (agent default — unconfirmed)
Tooltip, lore and dialog body text runs 45-75 characters per line. Set max-width from
MeasureTextSize of a 65-character sample at the role's font, and put long prose in a
bounded scroll region. Multi-line body needs open leading: USS has no line-height, so
check the FontAsset's Face Info line height (about 1.3x the point size or more for body)
and use -unity-paragraph-spacing between paragraphs. USS cannot justify text, so there
are no rivers to police.
UI-LIGHT-ON-DARK-COMPENSATION (agent default — unconfirmed)
Light text on a dark surface takes slightly more tracking or one weight step. USS has no
line-height, so tracking and weight are the only levers, and letter-spacing must be
measured (USS-UNITS-LIE in the HTML/CSS mock to UI Toolkit page).
UI-TEXT-EXPANSION-BUDGET (agent default — unconfirmed)
A slot that will be localized reserves 30-40% of width beyond the English string, or is
designed for the per-locale short form (CAPS-ARE-LOCALE-DATA in the uss uxml page). This
complements TEXT-CHANGES-DO-NOT-RESIZE-THE-SHELL (the UI design page), which keeps the
shell still. This rule keeps the text readable inside it. Strings are whole, translatable
messages: never stitch a sentence from fragments ("You have " + n + " gold"). Use one
string with named arguments (Unity Localization Smart Strings or an equivalent) so
translators can reorder it and pluralise correctly.
UI-TYPEFACE-HAS-A-REASON (agent default — unconfirmed)
Each face (display and text) has a one-line reason tied to the game's world or the screen's mode. The UI Toolkit default runtime theme font (Inter SDF) is fine for Editor tooling and Designer Console tabs. In shipped player UI it is the default-font tell unless the reason line chose it. The same goes for the training-data roster (Inter, Roboto, Geist, Space Grotesk, Plus Jakarta Sans, DM Sans, IBM Plex, Fraunces, Playfair, Cormorant, Recoleta; JetBrains/Geist/Fira mono). Avoid an oversized italic serif as the title voice by reflex, and avoid a stand-in system face (Arial Black, Impact, LiberationSans) used because it was installed. Any face is allowed with a reason; developer references win.
[AS: R-06, Part 1 "Typeface Chosen Without Reason"]
UI-TRACKING-AND-CASE-BOUNDS (agent default — unconfirmed)
Measured tracking (USS-UNITS-LIE in the HTML/CSS mock to UI Toolkit page) stays within these bounds. Never tighter than the point where letters keep their shapes (about -0.04 em equivalent; -0.02 to -0.03 em usually reads better). Body and multi-line text gets no wide tracking (above about 0.05 em). Wide tracking is for short uppercase labels only, and not on every label. Text that reads as a sentence (about 80+ characters) is never all caps. Caps live in the string table (CAPS-ARE-LOCALE-DATA in the uss uxml page) and are for short labels and titles.
[AS: R-06, Part 1 "Generic AI Typography"]
UI-TEXT-SIZE-FLOOR (agent default — unconfirmed)
Measure rendered size at the target device's resolution and PanelSettings scale, not the authoring px. Floors at desktop or phone viewing distance: functional text (buttons, labels, list cells, timers) ≥ 11 px equivalent, body and tooltip text ≥ 14 px, with 16 px preferred. TV or couch play (10-foot UI) scales these up. Check the platform holder's guideline; this rule doesn't invent a console number. If the project offers a text-size or UI-scale setting, every screen passes the existing fit sweep at its largest step.
[AS: antislop-human "Text That Cannot Zoom"]
UI-NUMBERS-HOLD-THEIR-COLUMN (agent default — unconfirmed)
A changing number (score, ammo, currency, timer) and a numeric column (a count, a price,
a leaderboard) use digits that share one advance. USS has no font-variant-numeric.
Measure the TextCore FontAsset: the advances of 0-9 match, or that string uses a face
whose digits do. The column sits on the end edge (the right edge in a left-to-right
layout). The slot's shell still follows TEXT-CHANGES-DO-NOT-RESIZE-THE-SHELL in
the UI design page.
[LINT: tabular-numbers, numeric-alignment]
Colour
UI-PALETTE-CAP (agent default — unconfirmed)
Use 2-3 core hues plus one accent, with neutrals excluded from the count. State colours (danger/warning/success/info) and data or rarity colours are a separate, documented token set, not extra accents. Panels, rails and toolbars may use a second neutral layer slightly warmer or cooler than the content surface. Pick dark or light from the play scene (who is playing, where, under what light), not from "games are dark". Note that a warm cream or beige ground chosen as the "tasteful" default is itself a tell.
[AS: R-29, R-21, Part 1 "Dark Mode Default for No Reason"]
UI-ACCENT-IS-A-SIGNAL (agent default — unconfirmed)
The accent marks the primary action, the current selection and live state only. Never use it as decoration, and never put full saturation on an inactive state. Zero accents reads sterile, and an accent everywhere is slop.
[AS: Part 3 one deliberate accent]
UI-STATE-COLOR-VOCAB (agent default — unconfirmed)
One state token set (hover, focus, pressed, selected, disabled, error, warning, success, info) with the same meaning on every screen of the project.
UI-NO-GRAY-ON-COLOR (agent default — unconfirmed)
Secondary text on a coloured surface is derived from that surface's hue or the foreground. Never use a neutral gray there. Prefer explicit colour tokens over translucent text, because alpha renders differently in a Linear project (PORTED-RGBA-ALPHA-IS-3X-STRONGER-IN-LINEAR in the uss uxml page).
UI-COLOR-NEVER-ALONE (agent default — unconfirmed)
Every colour-coded state also carries a shape, icon, text or position cue, for colour-blind players. ONE-VISUAL-CHANNEL-PER-MEANING (the HTML/CSS mock to UI Toolkit page) keeps meanings apart. This rule adds redundancy within one meaning.
[AS: antislop-human "Color-Only Feedback"]
UI-CONTRAST-NUMBERS (agent default — unconfirmed)
Amends the UI Toolkit runtime page pre-ship item 1, which says "readable" with no threshold. Body text needs ≥ 4.5:1 and large text ≥ 3:1. Non-text elements (control edges, icons, focus indicators, state borders, meter fills) need ≥ 3:1 against what is next to them. Compute the WCAG ratio in the existing C# contrast sweep from resolved colours composited the way the panel composites them (Linear vs sRGB), or from capture pixels. For text over the world, sample the worst case across the region. Never assert a ratio without computing it (the "grey-on-grey hallucination"). Every palette mode the game ships (colour-blind filters, high-contrast, alternate themes) runs the same sweep and passes it.
[AS: R-25, R-34, antislop-human Non-Text Contrast, "Grey-on-Grey Hallucination"]
Surface and decoration
UI-DECORATION-NEEDS-A-JOB (agent default — unconfirmed)
Each of these needs a written job or a developer reference. The dose caps apply even when a job exists.
- Glow plates or text-shadow glow: at most 1-2 focal elements per screen, not every control.
- Foreground blur and glass, together at most 1-2 layers. USS
filter: blur(6.3+ docs; re-probe — USS-CAPABILITY-PROBE in the HTML/CSS mock to UI Toolkit page listsfilterunsupported on 6000.5) blurs that element's own pixels. Glass is a tinted plate, or a captured background baked by the art pipeline. There is nobackdrop-filter. - A soft drop shadow (a baked plate) on every panel. Elevation marks hierarchy only.
- Capsule badges ("NEW", "HOT", "BETA") with no real state behind them, and the full stack of capsule + thin border + glow + dot + caps.
- Status dots that mark nothing, and any dot with an endless pulse.
- A coloured left or right stripe wider than 1 px on cards or rows that encodes no state.
- An eyebrow or kicker label above a title that repeats the title, or section numbers 01/02/03 that carry no sequence.
- Sparkle, magic, robot or lightning glyphs as generic feature icons. In agent-originated UI, emoji or loose Unicode glyphs as the icon system: icons come from one drawn set with one stroke and weight.
- Monospace as a "tech" costume rather than for numbers, code or measurements.
- Default AI palettes (blue to purple, cyan or neon on black, purple-and-black, pastel blocks) and dot, grid, graph-paper or repeating-stripe backgrounds with no reason in the game's world. Grids belong on real map, blueprint or build-grid surfaces.
- Gradient-filled text (TextCore colour-gradient presets or
<gradient>rich text) on titles and numbers. Emphasis comes from size, weight or a solid accent. - A baked radial glow, halo or "spotlight" sprite behind a title or panel as atmosphere.
- Arrows or chevrons (→ ↗ ›) on almost every button. Keep them where they mean "goes somewhere" or "advances".
- A rounded icon tile stacked above a heading on text cards. Framed icons stay right where the icon is the content (ability, item, perk slot).
- Sparklines, progress rings, mini meters or soft rounded rectangles standing in for content that does not exist. A meter shows a real value or it does not ship.
- A fake terminal window or a blinking caret where no input exists, as a "tech" costume. Typewriter reveal in dialogue is a genre convention and stays when the direction uses it.
- A hard zero-blur offset block shadow outside a direction that actually chose that look (a developer arcade reference keeps it, per UI-DESIGN-BRIEF-WINS).
- An icon set picked for its stock library look (thin-stroke rounded packs) rather than drawn or chosen to fit the world.
- Stacking several of these at once (glass + glow + grid + mono + pills) is a tell even when each one has a reason. Pick one surface idea per screen.
[AS: Part 1, R-01, R-04, R-06, R-07, R-08, R-09, R-10, R-12, R-13, antislop-ui Decorative Elements]
UI-ICON-SOURCES (agent default — unconfirmed)
A UI Toolkit icon comes from one row below, and only after UI-DECORATION-NEEDS-A-JOB has already allowed an icon. That rule still bans emoji and loose glyphs as the icon system, still requires one drawn set with one stroke and weight, and still rejects a pack chosen for its stock thin-stroke look. This rule does not repeat those tests.
- One family per product. Do not mix sets. Record the source, the license, and the version (package version, git tag, or download date) beside the imported set.
- Keep that family's grid and stroke weight consistent with the type scale (UI-TYPE-ROLES).
- SVG sets import through the
com.unity.vectorgraphicspackage (aVectorImageon the element's background), or are baked to sprites and an atlas at the sizes the screen uses. A cut-out bake still follows RESAMPLE-ALPHA-PREMULTIPLIED in the HTML/CSS mock to UI Toolkit page. ThisVectorImageis an icon, not a picture built from primitives (UI-REAL-ART-OR-NONE). - lucide-animated is React and Motion only. Lordicon is Lottie. Neither runs in UI Toolkit, so both are motion references only: re-author the move with USS transitions under the existing motion rules (UITK-MOTION-VOCABULARY in the uss uxml page, UI-MOTION-HAS-A-JOB). Do not import the React, Motion, or Lottie file as the control.
- Simple Icons is brand logos only. See the license cell: CC0 on the project data does not waive trademarks.
- A premium tier needs a purchased license before those files are imported. A free preview, a low-resolution PNG, or a trial export is not that license.
Licenses below were read from the cited page. Re-read that page at import; the terms move.
| Library | What it is | License | Unity fit |
|---|---|---|---|
| Streamline (streamlinehq.com) | Icon families (line, solid, and others), SVG on free sets | Free sets: Streamline Free License, attribution to streamlinehq.com, 50 icons per project (free license). A set is CC BY 4.0 only when its own description says so. Premium sets, and SVG from a premium set, need a purchased Premium license (no attribution, 100 icons per project) (premium license). | SVG import or bake |
| Tabler Icons (tabler.io/icons) | Stroke SVG set on a 24 grid | MIT (LICENSE, Copyright 2020-2026 Paweł Kuna). | SVG import or bake |
| Heroicons (heroicons.com) | Outline, solid, mini, and micro SVG | MIT (LICENSE, Tailwind Labs, Inc.). | SVG import or bake |
| lucide-animated (lucide-animated.com) | Animated React components, Motion, Lucide-style drawing | MIT (LICENSE, Copyright 2024-2026 pqoqubbw). | Motion reference only |
| Lordicon (lordicon.com) | Animated icons, Lottie export | Free: commercial use with attribution, "Animated icons by Lordicon.com" (free, attribution). PRO needs a purchased license; attribution is then optional (PRO). | Motion reference only |
| Simple Icons (simpleicons.org) | Brand-logo SVGs. Not a UI chrome family | Project data is CC0-1.0 (LICENSE.md). CC0 does not waive trademarks. The project disclaimer says one mark may carry its own license, so read that entry before import (DISCLAIMER.md). | SVG import or bake, logos only |
| Boxicons (boxicons.com) | 24-grid SVG packs (outline and filled) plus a brands pack | Free icons: CC 4.0, share and adapt, including commercially; the page says attribution is required and already included in the files (free license). Pro needs a purchased license (Pro license). The icon font is SIL OFL 1.1 and is not the UI Toolkit import. | SVG import or bake. The brands pack is logos, not the UI family |
UI-RADIUS-AND-ELEVATION-ARE-TOKENS (agent default — unconfirmed)
Radius comes from a small token set (control, panel, pill). Not everything is a pill. Each kit speaks one elevation language: a border or a baked shadow plate, not both on the same element (the "ghost card"). In USS a shadow is a bake, so each elevation level is one bake-script entry.
[AS: R-11, R-12]
UI-INNER-RADIUS-MATCHES-THE-INSET (agent default — unconfirmed)
When a rounded surface sits inside another with the same inset on every side, the inner
border-radius is the outer radius minus that inset, and it stays at 0 when the inset
is larger. The inset includes the border width. A pill whose radius is half its height
stays the capsule rule in the UI Toolkit runtime page.
[LINT: nested-radius]
UI-REAL-ART-OR-NONE (agent default — unconfirmed)
Large pictorial visuals (title art, character portraits, splash and empty-state
illustrations) come from the art pipeline as real sprites, or they are left out. Never
assemble a scene from a pile of Painter2D/generateVisualContent primitives or a
VectorImage of basic shapes, and never use generic stock illustration (Undraw-style
figures, 3D blobs) with no link to the game. Painter2D stays first-class for geometry:
meters, rings, graphs, range arcs, procedural frames. Do not fake an organic silhouette
with a circle or polygon mask (overflow: hidden + border-radius). Ship an alpha
cut-out sprite instead, though a round avatar crop is plain geometry and fine. A
background sprite must reach the screen: not buried under a near-opaque tint plate or set
to near-zero opacity. An image slot is never empty or showing a missing-sprite fallback.
[AS: R-22, Part 1 "Generic Illustrations"]
States and copy
UI-CONTROL-STATE-SET (agent default — unconfirmed)
Every interactive kit control defines default, hover (on pointer platforms), focus,
pressed, disabled, and selected if it can be selected, plus loading and error when it
starts async work. On screens navigable by keyboard or gamepad, focus shows a visible
indicator at ≥ 3:1 that is not hover-only. USS notes: :focus needs focusable = true.
pickingMode is the pointer switch: Position when the control takes hits, Ignore when
it must let them through. :hover and :active need Position, because they come from
pointer events. The inverse-trap bullet in the UI Toolkit runtime page still groups :focus
into that same pointer requirement. Draw the indicator as a colour change on a border width that
is already reserved, or as an absolute overlay child, so focus never moves layout
(UITK-SELECTION-KEEPS-CONTROL-METRICS in the uss uxml page). State specificity follows
HOVER-RESET-SPECIFICITY in the HTML/CSS mock to UI Toolkit page.
[AS: R-32, antislop-human Focus & States]
UI-STATES-NAME-CAUSE-AND-NEXT-ACTION (agent default — unconfirmed)
Data views (inventory, friends, lobby browser, shop, leaderboard, mail) ship empty, loading and error content. Empty says why and gives the one action that fills it. Loading says what is loading. Error says what failed and how to recover. First run, filtered to nothing, and offline/no permission are different states. On Operate screens, prefer reserved-slot or skeleton loading over a lone centred spinner. That also keeps geometry still (STATE-CHANGE-NEVER-MOVES-LAYOUT in the uss uxml page). Never fake progress (a bar that animates without a real fraction) and never delay a finished operation to stage a flourish. Routine success feedback is brief.
[AS: R-27, antislop-ui "Placeholder Empty and Loading States"]
UI-COPY-NAMES-THE-ACTION (agent default — unconfirmed)
A button names its verb and object ("Equip", "Start match", "Leave lobby"), not a bare "OK" or "Submit". A destructive confirm names the consequence. Errors name the problem and the recovery, with no internal error code as the main message (a code may follow as support detail), and never promise a cause the game cannot know. No generic CTAs by reflex ("Get started", "Learn more", "Explore", "Discover"). Icon-only controls carry a visible label or tooltip and an accessible name. Placeholder text in an input field is an example, not the label.
[AS: R-15]
UI-NO-INVENTED-NUMBERS (agent default — unconfirmed)
No invented claims in shipped UI: player counts, ratings, reviews, deltas or
testimonials. A shipped number comes from the real resolver
(CARD-SHOWS-THE-COMPARABLE-NUMBER in the HTML/CSS mock to UI Toolkit page) or is not shown. In a proposal
or draft screen, demonstration data (item names, stat values, a filled inventory) is
design material: author it at full fidelity, make it plausible for the game, and note it
as sample data in the proposal. It must be specific to this game: never generic filler
(John Doe, Player1, "Lorem ipsum", "Item Name") and never stub markers like [REAL DATA].
No invented trust or performance claims either ("anti-cheat protected", "60 FPS
guaranteed", "#1 rated"), and no friends or activity feed populated with made-up players.
[AS: R-17, R-36, R-38, antislop-ui "Stat Cards With Invented Numbers"]
UI-COPY-HYGIENE (agent default — unconfirmed)
Player-facing strings carry no marketing buzzwords or empty AI vocabulary: revolutionary, seamless, ultimate, next-gen, cutting-edge, game-changing, world-class, powerful, effortless, intelligent, AI-powered, streamline, empower, supercharge, unlock, elevate, delve, journey, testament, landscape, robust. They also carry no decorative emoji and no chatbot-style closers ("Hope you enjoy!", "Let me know"). Developer call, not adopted: antislop's outright em-dash ban (R-02). See UI-COPY-NO-AI-CADENCE for the saturation check.
[AS: R-16, antislop-copywriting "Empty AI Vocabulary", "Chatbot Closers"]
UI-COPY-NO-CEREMONY (agent default — unconfirmed)
UI, store, patch-note and tutorial text says the thing plainly. The following are all cut:
- significance inflation ("a new era of", "the future of", "a testament to");
- authority tropes ("at its core", "what really matters");
- fake-candid openers ("Honestly?", "Here's the thing");
- signposting ("Let's dive in", "Here's what you need to know");
- weasel attributions ("players say", "experts agree");
- speculative gap-filling ("likely", "is said to") where a fact is unknown;
- generic upbeat closers ("The future looks bright", "Exciting times ahead");
- filler phrases ("in order to", "it is important to note");
- stacked hedges ("could potentially").
Name the actor when one exists: no actorless passive ("the decision was made") and no abstraction with a human verb ("the shop understands what you need"). System messages where the actor is irrelevant ("Match saved") are fine.
[AS: antislop-copywriting Tone & Voice, Honesty & Evidence, Filler Phrases, Excessive Hedging]
UI-COPY-NO-AI-CADENCE (agent default — unconfirmed)
Look for rhythm tells repeated across a screen or document. One use is fine; the pattern is the tell:
- forced rule-of-three lists;
- "not just X, it's Y" negative parallelism and tacked-on "No X." negations;
- "X. No Y." / "Not a feature. A platform." rebuttal lines;
- aphorism formulas ("X is the currency of Y");
- staccato fragment runs;
- false ranges ("from first match to final boss, and everything in between");
- "X is theater" framing;
- em-dash saturation (roughly 8 or more at about one per 500 characters of copy).
Carve-out: in-world flavour, lore and character voice may use any of these on purpose when the direction gives that voice. The rule polices system and marketing copy.
[AS: antislop-copywriting Rhythm & Structure, R-02 (as advisory)]
UI-COPY-FORMATTING-RESTRAINT (agent default — unconfirmed)
Rich text emphasis is rare:
- no
<b>on every key term; - no scare quotes around ordinary words;
- no ALL-CAPS clauses inside a sentence to shout urgency;
- no "Header: restates the item" bullet lists in patch notes or tooltips;
- no emoji leading headings or bullets.
Emphasis is written into the sentence, and one accent word may keep its caps or bold.
[AS: antislop-copywriting Boldface Overuse, Excessive Quotation Marks, All-Caps Emphasis, Inline-Header Lists, Emojis in Headings]
UI-ONE-TERM-PER-CONCEPT (agent default — unconfirmed)
Each game concept has one noun and one verb everywhere (Scrap is never also "Salvage" or "Parts"; "Equip" is never also "Use" for the same act). Don't cycle synonyms for variety. Keep a short term glossary in the project docs once a term spans screens, and define unavoidable jargon inline (tooltip or first-use line).
[AS: antislop-copywriting "Synonym Cycling"]
UI-SAY-IT-ONCE (agent default — unconfirmed)
A fact appears once per panel, in the slot where it belongs. The same literal status or label rendered three or more times in one card or panel is template wiring, not design. A heading that already explains the state needs no intro line that repeats it. Helper text answers a question the control raises and never restates the control.
UI-COPY-AUDIT-PASS (agent default — unconfirmed)
Before shipping player-facing text, run antislop's draft, audit, final loop. Ask "what makes this read as machine-written?" and "does it state any fact, name or number that is not in the source?", then revise. Judge clusters, not single tells. Do not flag or "fix" polish, formal words, a single em dash, one short punchy line, mixed register, or text inside quotes, names and titles. Preserve the signs of a real writer (specific odd detail, varied sentence length, real asides). When the developer supplies a voice sample, match it over these defaults. Don't over-sterilise: copy with no voice is as machine-made as copy full of tells.
[AS: antislop-copywriting "What NOT to flag", "Signs of human writing", "Voice calibration", "Draft, audit, final", "Do not over-sterilize"]
UI-ONBOARDING-RESPECTS-THE-PLAYER (agent default — unconfirmed)
Tutorials and first-run hints:
- teach by doing (prompt the real action) rather than walls of text;
- never block the game behind a skippable lesson;
- don't patronise or over-explain;
- celebrate completion briefly;
- can be dismissed with a "don't show again";
- unlock complexity gradually;
- never replay for a returning player.
Help or FAQ screens answer questions players actually ask; don't write template questions.
[AS: R-28 (adapted from FAQ to in-game help)]
UI-HONEST-OFFERS (agent default — unconfirmed)
On store, bundle and battle-pass screens:
- show as many tiers as the catalog really has, not three by reflex;
- only use a "Most popular" or "Best value" badge when it is literally true (computed value per currency, or real purchase data);
- only show strike-through prices and "% off" for a real prior price;
- only show countdown timers for real end times.
This sits beside UI-NO-INVENTED-NUMBERS and the Persuade mode.
[AS: antislop-ui "Most Popular Pricing Card", "3 Pricing Tiers", R-36]
Motion
UI-MOTION-HAS-A-JOB (agent default — unconfirmed)
Every animation acknowledges input, explains a state or spatial change, or is the screen's
one authored moment. Durations: 100-150 ms for feedback, 150-300 ms for routine state,
300-500 ms for a panel or overlay, up to 800 ms only for an authored focal entrance. Exits
run faster than entrances. Operate screens get no entrance choreography on each open
(opening is show + refresh, per UITK-PREBUILD-REUSE in the HTML/CSS mock to UI Toolkit page). Stagger is
for lists only, with a capped total delay. USS: these map to transition-duration, and
UITK-MOTION-VOCABULARY (the uss uxml page) picks the tool. Content is visible at rest:
an entrance starts from an already-visible state or is guaranteed to finish
(UITK-FADE-IN-NEEDS-TWO-TICKS in the uss uxml page). An element must never be left at
opacity 0 or off-screen because a transition did not fire, and the sweep checks for it.
Do not give every panel the same fade-up entrance.
[AS: R-19, antislop-ui Motion]
UI-IDLE-LOOPS-ARE-EARNED (agent default — unconfirmed)
A looping idle motion (float, pulse, shimmer, marquee) exists only where the direction or reference has it, or where it marks a real live state. At most 1-2 per screen. Once it exists, "Idle motion must be READABLE" (the HTML/CSS mock to UI Toolkit page) governs how it reads, and UITK-PERF (the uss uxml page) stops it while hidden. This reconciles both sources with the existing idle-motion rule.
[AS: "Endless Pulses and Loops"]
UI-REDUCED-MOTION-CLASS (agent default — unconfirmed)
A project reduced-motion setting toggles one root class. The class removes spatial movement (translate, scale and rotate loops, shakes, parallax) and keeps the opacity and colour changes that carry state. USS has no media queries, so this is setting-driven. Each screen sheet carries its own override (LATER-SHEET-OUTRANKS-THE-THEME-SHEET in the uss uxml page).
Overshoot easing (ease-out-back) stays allowed as game "juice" when the direction calls for it. It is not the Operate default. Impeccable's blanket "no bounce/elastic" is not adopted.
UI-MOTION-SPEC-NAMES-THE-SAMPLES (agent default — unconfirmed)
A motion plan states numbers. Each move names its duration, its easing or the damping of a
code spring the project already uses, and the samples that prove start, peak and end: a
frame number, or a resolvedStyle, pose or VFX value. UI-MOTION-HAS-A-JOB still sets the
duration bands. An overshoot the direction asked for is a named peak. Ported reference
numbers win (UI-DESIGN-BRIEF-WINS). The same plan names the tool
(UITK-MOTION-VOCABULARY in the uss uxml page, or a code tween the project already uses),
the Unity and package version that was checked, the parameter the API actually reads, and
each known trap by its existing rule id and file. The other spoke states the trap. Re-run
USS-CAPABILITY-PROBE (the HTML/CSS mock to UI Toolkit page) when the editor version changes. Game-feel
poses and VFX lifetimes use the same line, and the measurements those spokes already name
(SHAKE-IS-VERIFIED-BY-POSE-LOG in the animation page,
VENDOR-VFX-DURATION-IS-A-LIFETIME in the rendering page).
[FLX: numeric prompt; pin the version and the parameter the API reads]
UI-MOTION-EACH-TRANSITION-IS-A-PASS (agent default — unconfirmed)
Check each transition on its own. Stills or captures at the planned frames, and a
measurement (per-frame resolvedStyle, a pose log, or the VFX sample the existing rule
names), both have to match the spec. A look at the whole clip does not clear one move. A
screenshot alone does not clear a value a sample can read
(UITK-POSE-OUTLIVES-ITS-TRANSITION and UITK-CLAMP-CUTS-THE-TWEEN in
the UI Toolkit runtime page; UITK-FADE-IN-NEEDS-TWO-TICKS in the uss uxml page). Game-feel
and VFX get the same pass. Calling the move done waits on that evidence.
[FLX: frame-by-frame stills and measurements; each transition is its own pass]
Checklist
Items 9-16 of the UI pre-ship checklist in the UI Toolkit runtime page point at the rules
above. They carry the same agent default — unconfirmed status. Item 1 of that checklist
points at UI-CONTRAST-NUMBERS for the numeric thresholds. Item 16 points at
UI-MOTION-SPEC-NAMES-THE-SAMPLES and UI-MOTION-EACH-TRANSITION-IS-A-PASS.
USS translation quick reference
Checked against Unity 6.3/6.4 docs. filter and aspect-ratio are listed as
unsupported by USS-CAPABILITY-PROBE (the HTML/CSS mock to UI Toolkit page, 6000.5 measured set) and by the
"no aspect-ratio" bullet in the uss uxml page. The Unity 6.3+ docs list both. Re-probe
with the doc syntax (filter: blur(4px), aspect-ratio: 16/9) before treating either note
as current. The same docs list -unity-material; re-probe that against
UITK-NO-SHADER-ON-VISUALELEMENT in the uss uxml page on the editor version in use.
| Source idea | USS / UXML / C# form |
|---|---|
spacing scale + gap |
:root tokens + symmetric half-margins (no gap, no calc) |
| type scale, weights | font-size tokens; one TextCore FontAsset per weight |
line-height, text-transform |
none in USS; tracking/weight only; casing lives in the string table |
prefers-reduced-motion, media queries |
none; root class driven by a settings value |
| box-shadow, gradients, glow | baked 9-slice plates (existing bake contract); text-shadow for text glow |
| blur | filter: blur blurs the element's own pixels (6.3+ docs; re-probe — the 6000.5 probe lists filter unsupported) |
| glass | a tinted plate, or a captured background baked by the art pipeline. No backdrop-filter |
focus ring / outline |
no outline; border-colour on a reserved border width, or overlay child |
| aspect-ratio | listed in 6.3+ docs (re-probe; the uss uxml page still sizes both axes in px); else fixed px on both axes |
| hover/focus/active | :hover :focus :active :checked :disabled :enabled; classes for selected/loading/error |
| transitions | supported; no @keyframes (UITK-MOTION-VOCABULARY in the uss uxml page) |
TEXT-CHANGES-DO-NOT-RESIZE-THE-SHELL (HARD) — text stays inside its assigned space
Even small text changes must not resize their container and cascade through its parents. Apply this to every UI framework.
- Parents own geometry; text consumes it. Give shell regions, control rows, buttons, status slots and overlays explicit sizing constraints. A label's preferred/intrinsic size must not determine an ancestor's size or move siblings. Responsive changes come from the viewport or an explicit layout mode.
- Choose overflow deliberately. Reserve a fixed line budget for labels and statuses; truncate with access to the full value, or wrap/scroll inside a bounded region. Long prose can flow inside its own scroll area. Never hide essential instructions or actions just to make a screenshot fit. Reserve scrollbar space so appearing/disappearing scrollbars do not shift adjacent content.
- Break the entire sizing chain. In web flex/grid layouts use zero minimum
sizes, bounded tracks/slots and appropriate overflow;
width: 100%alone does not defeat intrinsic minimums. In UI Toolkit bound flex children and text overflow. In uGUI use parent-owned RectTransforms/LayoutElements; do not let Text/TMP preferred sizes or ContentSizeFitters drive structural ancestors. Text autosizing, if needed, is bounded within the assigned rectangle. - A wide absolute overlay child must not determine the centred status parent's width. Give the parent its own geometry and position the extras from that stable shell; measure the parent and its siblings as the overlay text changes. (Refined.)
- Sweep the whole shell, not just the reported label. Cover navigation, headers, footers, statuses/errors, versions/counts, paths, dropdown selections and options, content panes, dialogs and their action rows. Include hidden and loading/empty/error states. Do not broaden a requested product sweep to other products without scope authorization.
- Prove the geometry in the running UI. Replace short text with empty, long, multiline and unbroken values; vary glyph/font metrics slightly and exercise real state changes. At minimum/default/large layouts and supported DPI scales, compare ancestor/sibling bounds before and after. Shell/control positions must remain unchanged (allow only measured subpixel rounding); overflow must remain readable and actions reachable. A fixed outer window alone is not a pass. Diff only what text can move: growth inside a bounded scroll's content is allowed, and elements that change on their own (animated fills, scrollbar thumbs) are sampled twice and excluded (refined 0.6.14).
Why: content-driven minimum sizes and reflow propagate up the layout tree, so a harmless status or font change can move controls across an otherwise stable UI.