Files
shd.build/docs/DESIGN_SYSTEM.md
T
JB 2e868d86ff
Validate and deploy production / validate (push) Successful in 18s
Validate and deploy staging / validate (push) Successful in 18s
Validate and deploy production / deploy (push) Successful in 1m1s
Validate and deploy staging / deploy (push) Successful in 28s
feat: refine ISAC design and prepare production deployment
2026-07-26 12:25:47 -04:00

1542 lines
91 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SHD.build Design System
This document defines the visual language for SHD.build, the independent
Division Resurgence community for builds, guides, game data, news, and
discussion. New pages should feel like part of the same tactical intelligence
network as the admin dashboard, while public pages may use more space and larger
editorial typography.
## Design principles
1. **Tactical, not military cosplay.** Use operational language, compact information density, restrained status indicators, and precise alignment. Avoid camouflage, distressed textures, or decorative weapon imagery in utility interfaces.
2. **Data first.** Hierarchy comes from grouping, typography, borders, and spacing. Admin tools should resemble a dependable control surface rather than a collection of marketing cards.
3. **Orange means action.** SHD orange identifies primary actions, active navigation, focus, and important values. It should not cover large areas.
4. **State is always visible.** Loading, disabled, active, archived, configured, error, and success states need both visual and textual indicators.
5. **Square and engineered.** Panels and controls use square corners, fine borders, tight spacing, and deliberate grid alignment.
6. **Links use interface feedback, not underlines.** Links do not use native text
underlines. Communicate hover and focus through SHD accent color, borders,
brightness, and the required focus-visible outline.
### Routed error console
Expected HTTP errors and unexpected server faults use one shared SHD exception
console inside the normal application shell. The console always displays the
numeric status, an operational title, a safe explanation, request path, node,
and context-appropriate recovery controls. Common statuses receive distinct
language: authentication for 401, clearance for 403, lost location for 404,
conflict for 409, saturation for 429, and service/fault language for 5xx.
Orange identifies recoverable request errors and actions; red identifies server
faults. Never display stack traces, database messages, credentials, request
queries, or raw unexpected exceptions to users. Unexpected server faults receive
a short reference ID from the global error handler for support correlation while
the complete error remains server-side. If the root application shell itself
cannot render, `src/error.html` provides a dependency-free SHD fallback surface.
Error controls retain square geometry, focus-visible treatment, pressed motion,
plain-language labels, and stacked mobile recovery actions.
### Contact relay and legal requests
Public support uses the SHD Contact Relay surface: a broad transmission form
paired with a compact destination guide. Two-to-six destinations use the shared
segmented-choice pattern. Signed-in identity details may be prefilled, but the
public form remains available to people who cannot sign in. Discord community
access is a distinct provider-colored card; it supplements rather than replaces
the durable contact channel.
The Contact hero explicitly resets the global site-header height: semantic page
`header` elements must use content-driven height and spacing rather than inherit
the 76-pixel navigation-shell constraint. Its title, description, and Discord
card align within a minimum 230-pixel desktop band before the form grid begins.
The Contact page itself remains content-height-driven so high-resolution browser
zoom does not multiply a viewport minimum into a large empty region above the
footer.
The application frame is a full-viewport vertical flex stack. The shared footer
uses automatic top margin, so it rests against the bottom edge on short routes
and follows normal document flow after longer content. Individual pages must
not add spacer heights solely to position the footer. The frame compensates for
`--shd-display-scale` when calculating its viewport minimum so high-resolution
browser zoom does not push a short-page footer below the visible edge.
Community access in the shared footer uses a compact Discord-branded action
with the official symbol, provider blue surface, explicit external-link text,
and visible hover, focus, and pressed feedback. Keep provider branding separate
from the orange links reserved for SHD navigation and legal resources. Support
and legal links form a compact horizontal rail that may wrap at intermediate
widths and becomes a two-column touch-friendly grid on narrow mobile screens.
The footer repeats the complete SHD brand lockup used by the global header:
double-ring SHD mark, white `SHD`, orange `.build`, and the restrained
`RESURGENCE COMMUNITY` descriptor. Do not substitute a plain text wordmark or
color the complete product name orange.
### Community platform positioning
SHD.build is a full Division Resurgence community, not solely a loadout tool.
Primary product language and global navigation must represent five durable
channels: News, Database, Builds, Guides, and Builder. Loadout-specific terms
remain appropriate inside build routes, but the global brand descriptor,
homepage metadata, hero, sign-in introduction, and footer describe the broader
community platform.
The homepage uses a community-channel grid to establish this information
architecture. Each channel has an operational code, plain-language purpose,
and explicit destination. New channels should extend this reusable pattern
rather than turning the navigation into an undifferentiated list. Honest
launch-state pages may describe planned capability, but must distinguish queued
features from live data. Database landing metrics use approved active records,
not invented activity counts.
On desktop the five public destinations remain visible in the global
navigation. Mobile navigation numbers destinations dynamically so adding a
channel cannot collide with Inbox, Profile, or ISAC codes. Community channel
cards collapse from four columns to two and then one; hover color alone never
replaces their textual destination labels, focus-visible treatment, or pressed
feedback.
Authenticated desktop account destinations are consolidated into one
agent-status menu at the right edge of the global header. Its trigger combines
the agent avatar, display name, online state, unread count, and an explicit
expanded marker. The menu contains My Profile, Notifications, and Sign Out;
these actions are not duplicated as standalone desktop header links. The menu
uses the same square, compact control-surface treatment as the rest of the
header, with orange reserved for the open/current state and unread count and
danger color reserved for the Sign Out hover state.
The account trigger exposes its expanded and controlled-menu state. Opening the
menu moves keyboard focus to its first destination; Escape closes it and
returns focus to the trigger, while an outside click dismisses it. Mobile keeps
Profile, Notifications, and Sign Out as explicit navigation actions and shows
the avatar in the signed-in identity block. Avatar imagery supplements rather
than replaces the textual identity, state, and unread indicators. User avatars
are always rendered as circular portraits with the restrained orange SHD ring
used by the Agent Profile; the source image remains square and uses
`object-fit: cover` so authenticated surfaces crop it consistently.
When unread notifications exist on mobile, repeat the compact orange count on
both the menu trigger and the Notifications destination. The trigger badge sits
outside the upper-right control edge without changing the hamburger geometry.
Its accessible name includes the full unread count; the visible badge is hidden
from assistive technology to avoid announcing the count twice.
Oversight reviewers receive a permission-aware cadence console inside
Notification Preferences. Each available review queue is shown independently
and uses the shared segmented radio pattern for Immediate, 15-minute, one-hour,
or daily delivery. Do not expose categories the agent cannot review.
Consolidated delivery groups queue items rather than issuing repeated messages;
daily delivery targets 10:00 AM in `America/New_York` so the Eastern wall-clock
time remains stable across daylight-saving transitions. On mobile, cadence
segments wrap to two columns and each review category becomes its own row.
ISAC System Configuration is a distinct navigation domain from System
Oversight. It contains operational configuration such as Automated Messages and
read-only observability such as System Metrics. Automated-message editors use
the standard horizontal edit lifecycle: successful edits close the editor,
validation preserves it, and event triggers remain explicit. Content, enabled
state, post-event delay, primary destination, and up to eight action callouts
are displayed as structured fields rather than raw JSON.
Automated ISAC messages may expose multiple square action callouts in the
notification inbox and detail view. Callouts wrap on desktop and become
full-width touch targets on mobile. Each retains a specific verb and
destination; do not replace the set with a generic “open” action. External
community actions such as Discord use secure HTTPS destinations while internal
actions use routed paths.
Contact notifications use the mandatory ISAC visual identity because they are
operational inbound messages. The notification carries a concise preview and a
specific action to an authorization-checked full record. Full messages never
appear in URLs. Copyright requests add a yellow legal-detail subpanel, explicit
attestations, electronic signature, and a direct policy link. Do not visually
present a submission as a legal determination.
The DMCA policy reuses the legal-document/index layout. Copyright and takedown
links must remain visible in the global footer alongside Contact Us. On mobile,
contact columns stack, destination segments become full-width 44-pixel rows,
attestations remain readable, and reply/recovery actions occupy the available
width.
### Builder range controls
Configurable numeric rolls use the shared range-value control: a descriptive
label and current minimum/maximum above a synchronized range slider and direct
number input. The slider supports fast exploration while the number field
supports exact entry; both clamp to the currently calculated bounds. Focus uses
the SHD action border, active slider feedback uses the standard pressed
brightness treatment, and number fields retain the global select-all-on-focus
behavior.
When an equipment level changes, preserve the configured roll's relative
position within its previous range instead of resetting it arbitrarily. Mark
unverified portions of a level curve with both `ESTIMATED` text and the
High-end yellow caution treatment. Controls stack the slider and direct input
at narrow mobile widths and must never create horizontal page overflow.
Equipment level uses this same synchronized control rather than a standalone
number field. Changing level remaps base and rolled values to the equivalent
relative position in the newly calculated range.
Gear and weapons always expose exactly two required rolled-Attribute positions.
Render both positions as persistent numbered rows; do not offer add or remove
actions. Already selected Attributes are disabled in the other position.
Use compact segmented radio controls for the small finite weapon-tier set.
Use the shared builder radio grid for visually identified Brand Sets and weapon
talents: transparent icons lead each square engineered option, text remains
visible, and the selected state uses the orange action edge plus an explicit
radio state. Talent grids become searchable when their available pool is large.
On narrow viewports option grids reduce to two columns and then one column so
labels and 44-pixel-or-larger targets remain usable.
## Tokens
The canonical tokens live in `src/app.css` under `:root`.
| Purpose | Token | Value |
| --- | --- | --- |
| Page background | `--shd-bg` | `#0a0c0d` |
| Base surface | `--shd-surface` | `#111415` |
| Raised surface | `--shd-surface-raised` | `#141718` |
| Primary border | `--shd-border` | `#303536` |
| Main text | `--shd-text` | `#ecebe5` |
| Muted text | `--shd-text-muted` | `#858b8d` |
| SHD action orange | `--shd-orange` | `#f6a821` |
| Telemetry text floor | `--shd-type-telemetry` | `12px` |
| Supporting copy floor | `--shd-type-supporting` | `14px` |
| Ordinary body floor | `--shd-type-body` | `16px` |
| Adaptive display scale | `--shd-display-scale` | `1` by default; stepped on high-resolution desktops |
### High-resolution display scaling
The application uses restrained, stepped interface scaling when the effective
CSS viewport is substantially wider than a conventional desktop. At 2200px the
complete interface scales to 115%, at 2800px to 130%, and at 3400px to 150%.
This preserves the established relationship between typography, controls,
tables, spacing, hit targets, and fixed-position surfaces instead of enlarging
text alone.
Use the shared `--shd-display-scale` token and the root-level `zoom` treatment;
do not add module-specific 4K font-size overrides. Responsive layouts below
2200px remain unchanged. Browser zoom and operating-system display scaling still
remain available to users and naturally change the effective CSS viewport.
High-resolution scaling must not introduce horizontal overflow, clipped fixed
surfaces, or a different semantic hierarchy. Test the standard desktop layout
and each affected wide breakpoint after changing this policy.
| Success | `--shd-success` | `#75c08c` |
| Destructive/error | `--shd-danger` | `#df8069` |
| Informational/defensive | `--shd-info` | `#70b9d6` |
Use the spacing tokens for new components. Prefer `8px`, `12px`, and `20px` gaps. Dashboard controls use the shared `40px` height when practical.
## Typography
SHD.build self-hosts two intentional type voices through Fontsource packages:
- **Barlow** is the operational family for display headings, navigation,
controls, data, labels, metadata, and general interface copy. Use the shared
`--shd-font-sans` token instead of declaring local sans-serif stacks.
- **Source Serif 4** is the editorial family for explanations, introductions,
guide-like prose, and longer descriptive passages. Use
`--shd-font-editorial`; do not apply it to controls, tables, metrics, or
telemetry labels.
Only the used Barlow weights (400, 600, 700, 800, and 900) and Source Serif 4
weights (400 and 600) are loaded. Both families are self-hosted, preserve
fallback stacks, and render immediately without a third-party font request.
The two-voice rule remains semantic: Barlow operates the system; Source Serif 4
explains it.
- Page and module headings are uppercase, heavy, and tightly spaced.
- Short operational labels, metadata, statuses, and compact actions use the
shared 12px telemetry floor. Their tracking must not exceed `0.1em`.
- Supporting descriptions use at least 14px with normal tracking. Ordinary
body and form copy uses the 16px body floor.
- High-resolution workspace scaling never justifies smaller source sizes:
density comes from spacing, connected borders, tone, and weight.
- Public/editorial copy uses Source Serif 4 for its restrained field-manual
character and stays within a 45–75 character reading measure.
- Never use low-contrast tiny text for information required to complete a task.
## Layout patterns
### Global mobile navigation
At viewports up to 900px, replace the hidden desktop link rail and account
controls with a clearly labeled square `MENU` control in the shared site
header. Activating it opens a right-side SHD navigation panel over a dismissible
backdrop. The panel includes Home, Discover, Builder, Guides, Sign In or My
Profile, and ISAC when authorized; authenticated agents also receive identity
state and Sign Out inside the panel.
Use numbered full-width navigation rows, explicit active-route text styling,
orange action indicators, and minimum 44px interaction targets. Opening moves
keyboard focus to the first route. Escape, backdrop activation, route changes,
and the header Close control dismiss the panel. The toggle exposes
`aria-expanded` and `aria-controls`, the panel has a distinct navigation label,
and reduced-motion preferences remove icon transitions. Do not make mobile
users rely on the logo or desktop-only account controls to reach core routes.
On phone-sized viewports up to 700px, ISAC module toolbars stack their action
area beneath the module title. A single Add action spans the available width;
multiple creation actions share an equal responsive grid. These controls use a
minimum 48px height, centered labels, and the standard pressed, focus-visible,
pending, and disabled treatments. Do not leave desktop-sized Add buttons
squeezed beside long module headings on mobile.
### Public pages
- Maximum content width: approximately 1400–1640px.
- Larger headings and more generous vertical rhythm are appropriate.
- Preserve the grid background and orange accents.
- Catalogue and editorial pages use `catalogue-hero` for an angular introductory
panel and may pair it with a compact operational control bar.
- Community build cards retain the colored top rule, abstract loadout artwork,
concise tags, agent attribution, and a full-width terminal action.
### Agent and authentication surfaces
- Account pages should read as an agent dossier: identity and access at the top,
activity metrics beside it, archives in the primary column, and security
controls in a narrower console column.
- Dense account consoles use a tabbed control surface for profile, credentials,
linked identities, and account lifecycle actions. Only one panel is shown at
a time; validation failures reopen the panel containing the failed action.
Closely related mutually exclusive workflows, such as selecting a preset
portrait or uploading a custom portrait, use a compact secondary tab row.
Tabs retain native button semantics plus tablist, tab, and tabpanel roles,
textual selected state, keyboard focus, and pressed feedback.
- Account Access uses a secondary method navigation for passkeys, verified
email, local password fallback, and linked identity providers. Each tab shows
a compact configured-method count and routes failed actions back to the
relevant panel. External identity connections live inside Access rather than
competing with it as a top-level account domain.
- Future external providers may appear as non-interactive planned rows so the
intended identity architecture is visible before credentials are configured.
Placeholders must be visually subdued, explicitly labeled `PLANNED`, and must
not imitate enabled buttons. Provider-compliant assets and controls replace
these rows only when the corresponding OAuth application is implemented.
- Provider unlinking does not assume that a password exists. The unlink control
is the provider card’s `LINKED` status badge. Hover and keyboard focus turn it
red and replace the label with `UNLINK`; activation replaces the full card
surface with an explicit Cancel / Confirm Unlink decision. Confirmation uses
the square SHD primary action with the orange action accent, while Cancel
remains a dark outlined secondary control. The authenticated
session plus this deliberate second action authorizes the request; the shared
server-side last-method guard remains authoritative. Future provider cards
reuse this pattern rather than adding provider-specific credential prompts.
- Authentication pages use a split frame: contextual SHD network information on
the left and the actionable secure-access console on the right.
- OAuth providers appear as connection nodes with a recognizable provider color;
SHD orange remains reserved for the primary local action and navigation.
- Authentication prioritizes passkeys, followed by one-time email magic links.
Discord remains a prominent provider option, while username/password is an
explicitly labeled optional fallback in a collapsed control. Passkey controls
name the platform interaction rather than exposing WebAuthn terminology.
- Verified email identities and registered passkeys appear as compact method
rows in Account Access. Every method displays its verification or device state,
registration date where useful, and protected removal behavior that prevents
an agent from deleting their last usable sign-in method.
- Local username and password are one optional authentication method, separate
from the public display name and the immutable internal account identifier.
Agents may remove both local credentials together after adding another method,
and may restore them later with a new unique login username and password.
- The “at least one sign-in method” rule is a server-side invariant shared by
self-service and ISAC actions. Passkeys, verified magic-link email, Discord,
and a complete local username/password pair each count as usable methods.
Controls may disable unsafe removal for clarity, but server validation remains
authoritative. Intentional soft account deletion is the sole exception.
- Linked identity nodes show provider display name, account handle or stable ID,
and an explicit textual `LINKED` state. Unlink controls sit beneath the node,
require credential confirmation, and remain disabled with an explanation when
unlinking would remove the user's only sign-in method.
- ISAC user dossiers reuse the same linked-identity node. Administrative
unlinking requires a scoped identity-management capability, produces an audit
event, and must never remove the user's only available sign-in method.
- On small screens, the split frame becomes a single column and account controls
move before archival content.
- Public display names and private login usernames are distinct. Display names
support normalized Unicode and relaxed visible characters; login usernames
remain unique, ASCII-only credentials and must never appear on public-facing
community surfaces. Authorized ISAC user operations may show both.
### Third-party identity buttons
- Follow the identity provider's current official brand and sign-in guidelines.
- Use official, unmodified assets downloaded from the provider's brand resources.
- Discord sign-in uses the official white Discord symbol on Blurple `#5865F2`
with the explicit label “Continue with Discord.”
- Discord account connection controls use the same official symbol and Blurple
treatment with the explicit label “Link Discord.”
- Sign-in pages group external providers into a compact, equal-weight provider
rail. Keep a short visible provider name beneath each mark; accessible names
and hover text are supplementary and do not replace required brand wording.
- Google uses its standard multicolor G on a white icon field, Apple uses a
white Apple mark on black, Discord uses its unmodified white symbol on
Blurple, and Steam uses its white linked-circle mark on Steam navy.
- Providers that are not configured are disabled, desaturated, and marked
`PLANNED`; they must not navigate, submit, or visually imply availability.
- When each provider is implemented, replace its placeholder with the
provider-rendered control or current pre-approved sign-in artwork where the
provider requires it. In particular, use Google Identity Services or its
approved icon-mode asset and Steam's published sign-in image.
- Preserve the provider's required clearspace, logo proportions, approved color,
readable contrast, and text/logo alignment even when the surrounding surface
follows the SHD theme.
- Provider buttons still require SHD.build's focus-visible, pressed, pending, and
duplicate-interaction protection where those states do not conflict with the
provider's branding requirements.
- Authentication uses one unified access screen rather than separate sign-in and
registration destinations. Verified email and first-time OAuth identities may
create an account; returning identities sign into the matching account.
- Password access uses an explicit `SIGN IN` / `CREATE ACCOUNT` mode switch
inside the fallback panel. Account creation separates the public display name
from the private ASCII login username.
- Passkeys are a returning-user method on this screen. New agents first establish
an account through verified email, OAuth, or local credentials, then enroll a
passkey from Account Access.
### Footer and legal surfaces
- Every routed page ends with the global SHD footer containing Terms & Privacy,
data-retention access, and the unofficial Ubisoft fan-site disclaimer.
- Legal pages retain the command-console visual language but prioritize readable
serif body copy, anchored sections, and restrained line length.
- Login security metadata is operationally visible in ISAC for 30 days. After
that period, IP, user-agent, normalized client/device fields, and credential
references and proxy-supplied approximate location are scrubbed while
non-sensitive event facts, categorical risk flags, and session outcomes may remain.
### Admin dashboard
- Use `admin-page`, `admin-command`, `admin-dashboard`, `admin-sidebar`, and `admin-content`.
- The administrative system is named ISAC. Separate navigation into explicit
operational domains such as User Operations and Data Management; visibility
must reflect server-provided capabilities.
- ISAC module navigation uses complete, game-facing names such as `Weapons`,
`Talents`, and `OS Protocols`. Do not expose internal abbreviations such as
`WPN`, `TLNT`, or `OSPR`. Canonical game icons may supplement visible labels
inside module content and editor controls, but the navigation rail must not
invent iconography merely to replace clear language.
- User Operations shows every supported authentication method as operational
metadata: local credentials, verified email, passkeys, Discord, and planned
providers. Identity-removal controls require `users.identities`, create audit
records, and preserve at least one remaining sign-in method.
- The Agent Registry prioritizes assigned roles over authentication-method
summaries. Authentication details belong in the individual agent dossier.
- ISAC authentication-method removal is always a two-step interaction contained
within the affected method row. The confirmation state explains the impact and
provides explicit Cancel and orange Confirm actions before submission.
- Agent dossiers consolidate operational controls into full-width tabs for
Roles & Permissions, Authentication, and Moderation. Keep dense controls in
their relevant tab instead of allowing one dashboard column to grow far beyond
adjacent content.
- Avatar removal uses the same in-card, two-step confirmation interaction as
authentication-method removal.
- Role and permission changes require a modal review step that summarizes the
complete selected role and direct-permission sets. Only the modal's orange
Confirm action submits the assignment.
- Access-review summaries are diffs: retained assignments use a neutral `KEEP`
state, additions use green `ADD`, and removals use red `REMOVE` with struck
assignment text. Added and removed items appear together in their respective
role or direct-permission column.
- Login, build/guide, and comment histories are independent support registries.
Each provides context-appropriate filters and namespaced pagination controls
so changing one history page does not reset the others.
- Each module starts with `module-toolbar`, followed by `module-kpis`.
- Editors use `dashboard-editor` and appear before the affected table.
- Data collections use `dashboard-table`, `dashboard-row`, and `table-heading`.
- Keep tables dense and horizontally scrollable rather than wrapping columns unpredictably.
- User dossiers pair access controls and moderation in a two-column console,
followed by compact login and content-history streams. Restriction state must
be textual and use the danger color; inactivity remains a separate activity
classification rather than a punishment state.
- Role names describe durable organizational responsibility. `Admin` is the
unrestricted administrative role; `Community Oversight` is reserved for
community content moderation and does not imply ISAC access. Operational
ISAC access is delegated separately through scoped roles and permissions.
- Roles is a separate User Operations registry. Role definitions
compose discrete capabilities through a dense permission catalogue with an
explicit selected count and textual `GRANTED` / `NOT GRANTED` states. The
built-in Admin role is visible but system-protected. Delegated access managers
must never be able to grant `system.admin`; both option visibility and
server-side validation enforce this boundary. Role mutations are recorded in
the audit log with their previous and resulting permission sets.
- Audit History belongs to a separate `SYSTEM OVERSIGHT` navigation domain.
Audit records are append-only operational evidence and show the operator,
categorical action, target identifier, timestamp, and expandable before/after
JSON snapshots. Search and category/action filters run before pagination.
The Auditor role grants read-only `audit.view` access plus entry to ISAC;
audit visibility never implies mutation, user-management, or game-data access.
- Supported audit events may expose a permissioned compensating-action control.
Reversion uses a danger-styled `ATTEMPT REVERT` entry action followed by a
modal that explains the original action, operator, target, and conflict
checks. The orange Confirm action is the only submission path. ISAC compares
current state with the original event’s after-snapshot before restoring the
before-snapshot; newer work blocks the operation. Successful reversions append
a new `audit.reverted` event and never alter the original history. `audit.view`
remains read-only; `audit.revert` is independently assigned.
- System Metrics is the second read-only System Oversight surface. It uses a
compact KPI row for headline process health and a two-column grid of dense
runtime, host, database, and service-volume panels. Refresh is explicit and
duplicate-safe; optional auto-refresh is visibly selected and uses a
restrained ten-second interval. Metrics must not expose environment values,
credentials, full filesystem paths, or other deployment secrets. The
`metrics.view` permission is independent of mutation access and is included
in the read-only Auditor role.
- Account deletion uses a collapsed danger zone, explicit identity confirmation,
and plain-language retention guidance. Soft-deleted accounts retain authored
content under a deleted-agent identity while releasing credentials, usernames,
external identity links, sessions, roles, and permissions.
- Threshold-based set bonuses use compact `effect-band` groups. Each threshold is
displayed as a fixed piece-count marker followed by its mapped stat and value,
keeping progression readable in editors and table summaries.
- Multi-value compatibility fields use a dense `selection-grid` of checkbox
rows with a visible selected-count summary. Keep the entire row clickable and
preserve native checkbox semantics for keyboard and assistive-technology use.
- When compatibility is conditional on another field, show only valid choices,
clear stale selections when the controlling field changes, and state required
minimum selections in both the section label and validation feedback.
- Tiered records with shared structure use one shared selector and side-by-side
value columns rather than duplicating the entire editor. Template-driven copy
must show live previews for every tier and document its placeholder syntax.
### Forms
- Labels are uppercase and letter-spaced.
- Inputs use dark surfaces, one-pixel borders, and clear disabled styling.
- Text selection uses the SHD orange selection token with dark contrasting text;
never rely on the browser's white or platform-blue highlight. Focused text,
number, search, select, and textarea controls use a one-pixel orange border,
while keyboard `focus-visible` adds a restrained one-pixel external orange
ring with a one-pixel offset. Do not stack inset accent edges with this focus
treatment. Carets and native checkbox, radio, and range accents use orange.
Browser-autofilled fields retain the dark SHD surface and light text.
- Related min/max fields remain visually paired.
- Small mutually exclusive option sets use a compact segmented radio control,
even when visually presented like selectable checkbox cells. Include an
explicit “None” option when the field is optional.
- Primary save actions sit at the lower right. Cancel is secondary.
- Destructive/archive actions are visually separated from save actions.
## Interaction rules
- Every interactive element needs a pointer cursor unless disabled.
- Buttons have hover, pressed, focus-visible, pending, and disabled states.
- Forms that send requests should use `PendingForm.svelte` or equivalent enhancement to prevent duplicate submissions.
- Pending buttons use `aria-busy="true"` and remain disabled until the request completes.
- Respect `prefers-reduced-motion`.
## Color semantics
- Orange: primary action, current location, selected operational value.
- Green: online, active, configured, or successful.
- Red: destructive action, error, or offensive classification when context requires it.
- Blue: defensive or informational classification.
- Gray: inactive, archived, unavailable, or supporting metadata.
Do not communicate state by color alone; include a label, icon, or pattern.
## Responsive behavior
- Collapse the dashboard sidebar into a two-column module grid below 900px.
- KPI tiles become two columns on small screens.
- Tier editors stack vertically on tablets.
- Tables retain a useful minimum width and scroll horizontally.
- Primary and secondary toolbar actions may expand equally on narrow screens.
## Adding a new admin module
1. Add the route beneath `src/routes/isac`.
2. Register it in the admin layout navigation.
3. Start with the module toolbar and KPI row.
4. Reuse dashboard editor and table classes.
5. Use the shared tokens instead of introducing near-duplicate colors.
6. Verify keyboard focus, pending behavior, empty states, mobile layout, and archived data behavior.
If a new interface needs a genuinely new pattern, document it here when introducing it.
## Design-system governance
Significant visual and interaction changes must update this document and the
shared implementation in the same change. A change is significant when it
introduces or materially revises a reusable token, component pattern, layout,
responsive rule, accessibility behavior, motion behavior, or state convention.
Small alignment corrections and isolated visual bug fixes do not require a
documentation change unless they establish a rule that other interfaces should
follow.
Repository agents and contributors should follow the maintenance checklist in
`AGENTS.md`.
### Agent avatars
Agent portraits use a circular crop in identity headers and square tactical
thumbnails in selectors. Preset choices appear in a compact four-column grid;
the active choice uses the orange action border and an explicit pressed state.
Custom images are normalized server-side to a metadata-free 256 × 256 WebP.
Selectors must retain visible hover, focus-visible, pressed, pending, and
disabled states, and collapse naturally with their containing account console.
ISAC user dossiers show the current portrait, distinguish preset and custom
sources textually, and place destructive custom-image removal behind the
existing moderation permission and pending-action pattern.
Moderation actions use full-width engineered controls: neutral outlined
buttons for reversible identity changes and muted red controls for destructive
or restrictive changes. Every control must expose pointer, hover,
focus-visible, pressed, disabled, and inline pending-spinner states; adjacent
navigation links use the same height, typography, and interaction motion.
### Weapon definition editor
Complex progression definitions use a stacked control-surface editor: identity
and fixed values first, then parallel tier cards, mutually exclusive talent
pools, fixed base bonuses, and the augmentation sequence. Tier cards retain
identical row ordering so values can be compared horizontally on wide screens
and stack without changing meaning on narrow screens. System-derived augment
steps appear as read-only numbered cells; editable effects use the shared
attribute/value row pattern. Exotic is a status layered onto a weapon family,
never a replacement for the family classification. Fixed base bonuses remain
visually separate from tactical augmentation effects. Exotic definitions add a
dedicated exotic-only talent after the two standard talent pools; their sixth
augmentation cell communicates that this talent advances to Tier II.
The exotic talent is owned by exactly one exotic weapon and is authored inline
inside that weapon editor, never selected from a shared global pool. Its
engineered sub-panel includes the talent name, shared description template,
Tier I/II previews, reusable triggers, and paired effect values. The Talent
Registry may display an owned exotic talent for discovery, but routes editing
back to its weapon rather than opening the shared-talent editor.
Large relationship sets use the searchable assignment control instead of
checkbox walls. It keeps assigned records visible as removable chips, filters
available records by name, renders at most thirty results at once, and exposes
counts for both assignments and matches. Mutually exclusive pools pass their
assigned IDs to one another as exclusions so invalid combinations cannot be
selected. Keyboard focus, pressed state, empty results, and overflow remain
explicit.
Where a relationship has a global slot invariant, filter each assignment
control at the data boundary instead of presenting cross-slot choices. Weapon
talents are classified once as Talent 1 / Top or Talent 2 / Bottom in the
talent registry; weapon editors only curate subsets from the matching slot.
### Game-semantic iconography
Canonical game icons are registered in `src/lib/game-icons.ts` and rendered
through `GameIcon.svelte`; do not repeat asset paths in individual features.
Use damage icons beside Blast, Pierce, and Shredding classifications, and core
attribute icons beside Firepower, Toughness, and Engineering values. The same
registry reserves the Bulwark, Combat Medic, Demolitionist, Tech Operator,
Vanguard, Legendary, and SHD-ticket assets for controls or data that explicitly
represent those concepts. Do not use semantic icons as unrelated decoration.
Interactive icon choices retain a visible text label, square engineered cells,
orange selected state, and hover, pressed, and focus-visible feedback. Icons
that repeat an adjacent visible label are decorative with an empty alternative;
standalone icons expose the registry's human-readable label. Preserve each
asset's aspect ratio with `object-fit: contain` and scale through the shared
`.game-icon` class rather than modifying source artwork.
### Item quality
Item quality is a global semantic scale: Standard uses
`--quality-standard` green, Specialized uses `--quality-specialized` blue,
Superior uses `--quality-superior` purple, and High-end uses
`--quality-high-end` yellow. Apply the matching `.quality-*` utility wherever
an item’s persisted quality is shown. Registry labels use `.quality-chip` so
quality remains explicit in text and never depends on color alone. These colors
identify item quality; orange remains reserved for actions and current
selection.
### OS Protocol editor
OS Protocol definitions separate shared secondary profiles from item-owned
progression. The twelve quality + attribute-type combinations are configured
once in a compact matrix and inherited by every matching protocol. Each profile
contains three fixed stat identities and editable values. Individual protocol
editors show the inherited profile read-only and store one unique primary stat
with a level-1 base value. That primary value scales linearly to exactly twice
its base at level 50.
Protocol identity forms stack the four-choice quality radio band above the
three-choice secondary-profile type band, with the protocol name occupying the
larger adjacent column. Quality choices use their global semantic color plus an
explicit text label and selected underline; profile types retain their
icon-backed labels. Both controls collapse below the name on narrow screens.
The shared secondary-profile matrix is collapsed by default because it is
configuration infrastructure rather than a frequent protocol-editing task. Its
disclosure header always exposes the configured total, uses a textual title,
and rotates the plus indicator when expanded. The full matrix remains keyboard
operable and follows the shared hover, focus-visible, and pressed states.
### Game-data icons
OS Protocols, brand sets, and talents use the shared game-data icon upload
control in their ISAC editors. Existing artwork is previewed and may be
replaced or explicitly removed. Registry identity cells render a compact
thumbnail before the record name and retain a neutral textual fallback when no
icon is configured.
Source artwork must be a transparent SVG, PNG, WebP, or AVIF no larger than
2 MiB. SVG is treated only as an upload source and never served directly.
Accepted images are normalized asynchronously to a metadata-free transparent
256 × 256 WebP with `contain`, preserving the full symbol without avatar-style
cropping. Image labels remain visible in the UI, so icons supplement rather
than replace textual identity. Upload, replace, remove, pending, validation,
focus-visible, and narrow-screen states follow the shared engineered form
language.
For compact editors such as brand sets, place the icon rail beside the record
name and its primary configuration rows instead of giving the upload control a
full-width band. Keep destructive archive controls isolated on the left side of
the action bar and use the established `.moderation-action.danger` treatment;
save and cancel remain grouped on the right.
Archived records that support restoration replace their destructive archive
control with a green outlined `.moderation-action.restore` control in the same
left-side position. The label must explicitly say “Unarchive” or “Restore”;
never communicate restoration by color or icon alone.
The dedicated unique-talent panel uses five interactive diamonds: no filled
diamonds is the base definition, and each selected upgrade adds one fifth of
the base until five upgrades show exactly twice the base effect.
Diamonds are both a progression preview and an explicit numeric state; expose
their upgrade number and `aria-pressed` state rather than relying on fill color.
Protocol talents own their effects but reuse the global stat and trigger
registries. Shared secondary values do not scale with the individual protocol’s
level. Store only the unique primary and talent base values, and derive their
progression consistently in calculation and display code.
### ISAC editor lifecycle
Successful saves have a consistent outcome throughout data management:
editing an existing record closes the editor and returns the user to the registry;
creating a new record keeps the editor open with a fresh blank definition for
rapid consecutive entry. Failed validation leaves the current editor and
entered values intact. Use a unique action result token so reactive handling
runs exactly once and does not create update loops.
Tiered-definition registries should show their rendered tier effects when the
text is operationally useful. Use compact wrapping description cells rather
than replacing meaningful data with a generic “configured” label; retain an
explicit textual missing state when a tier has no rendered description.
Talent mechanics are structured metadata, not free-form prose. Present optional
cooldown, duration, and maximum-stack inputs together in a compact three-column
mechanics strip that collapses to one column on narrow screens. Description
templates use numbered expressions (`{1}`, `{2}`) for tiered attribute effects
and named expressions (`{duration}`, `{max_stacks}`) for shared mechanics.
Named expressions are valid only when their corresponding value is defined.
Cooldown is never embedded in the description: render it consistently as
`Talent Cooldown: [value] seconds` beneath the talent identity or description.
Do not imply an absent mechanic with a zero value; show no cooldown line and
leave optional editor fields explicitly marked as not defined.
### Registry pagination
Every primary ISAC registry uses server-side pagination. The shared control
appears immediately above and visually attached to its table. It shows the
visible record range and total, previous/next navigation, a compact
window of page numbers, and row-count presets of 10, 25, 50, and 100. Existing
filter query parameters must survive page navigation, while changing page size
returns to page one. Summary metrics remain global or filter-global and must
never silently describe only the visible page. On narrow screens, retain
previous, next, current page, and page-size controls before optional page
numbers.
Registries with a meaningful name, title, or agent-identity column place the
shared server-side search inside this pagination rail. Submission resets the
page number while preserving the selected KPI filter and row-count preset;
page navigation preserves the query. Provide an explicit clear action whenever
a query is active. Search applies to the complete filtered registry before
pagination, including visible pending proposals, and never only to the current
page. Specialized oversight and history searches may remain in their richer
filter consoles when they search evidence beyond a single identity column.
At intermediate widths the search spans the pagination rail above its range
and navigation controls. At mobile widths all groups stack, the input retains a
usable width, and search, clear, previous, and next remain explicit keyboard
and touch targets.
### Registry metric filters
The KPI row above an ISAC registry is an operational filter surface, not passive
decoration. Use `KpiFilters.svelte` when each metric describes a meaningful
record subset. The first card returns to the complete current registry or search;
the remaining cards filter by useful classifications such as active state,
category, equipment behavior, or restriction state.
Filtering is server-backed and occurs before pagination. Filter links preserve
compatible search and page-size parameters, reset the page number, remain
bookmarkable, and expose the selected state with both orange emphasis and
`aria-current`. Cards require pointer, hover, focus-visible, pressed, and
textually identifiable active states. Counts describe the full search scope,
not merely the visible page. Do not use aggregate facts that cannot filter the
affected registry—replace them with actionable subsets.
### Image uploads
All image uploads use the shared SHD drop-zone treatment and expose three equivalent
selection paths: a file picker, drag and drop, and clipboard paste. The control shows
the selected filename and a local preview, but server-side format, size, transparency,
and normalization checks remain authoritative.
### Moderated data states
ISAC must clearly distinguish direct editing, contributor proposals, and read-only
access. Use a compact access banner above the module rather than changing the
registry layout. Pending proposal counts appear as orange numeric indicators in
data-navigation items. Proposal lifecycle states use text as well as color:
pending is orange/actionable, approved is green/configured, and rejected or
failed is red/archived. Review decisions always use the standard ISAC confirmation
surface and must never apply a proposal from a table-row click alone.
Eligible contributors enter ISAC data modules in read-only mode by default.
The access banner pairs the explicit `READ-ONLY DATA ACCESS` state with a compact
action that opens the versioned contributor guidelines. Contributor mode may
only be enabled after affirmative checkbox acceptance. Once enabled, the banner
changes to orange, explains that saves enter moderation, and offers a
`RETURN TO READ-ONLY` toggle. Disabling is immediate and reversible and does not
withdraw existing proposals. Use text for every mode state; color alone is
insufficient. At narrow widths, stack the explanation and full-width toggle.
Read-only mode is an inspection surface: hide add controls, row edit actions,
mutation forms, and editors opened through URL state. Do not merely dim or
disable them. Registry navigation, KPI filters, pagination, search, evidence,
and other non-mutating inspection controls remain visible and interactive.
The ISAC command-status cluster includes an explicit `DATA MODE` cell alongside
system, access, and environment. It reports the highest effective data authority
in this order: `ADMIN`, `OVERSIGHT`, `DATA MANAGER`, `CONTRIBUTOR`, then
`READ-ONLY`. Use a neutral treatment for read-only, orange for contributor,
blue for Oversight authority, and green for direct-management authority. The
label and value are mandatory because color is supplemental. At medium widths
the four cells become a two-column grid; on narrow screens the cluster spans
the command header width.
### Oversight proposal corrections
User-facing references to moderators and data approvers use the SHD term
`Oversight`. Distinguish `Community Oversight` from `Data Oversight` when a role
name must identify its operational domain.
Pending proposal review uses three equal decision controls: approve, correct,
and reject. `CORRECT` is limited to small text or numeric fixes. Present the
stored form fields in a compact two-column correction grid and identify their
original field names; long descriptions and structured JSON values use
textareas. Uploaded file evidence is visible but immutable. Every correction
must be separately saved and audit logged before approval, and material changes
should be rejected for contributor revision. Collapse the decision controls and
correction fields to one column on narrow screens.
Proposal evidence is reviewer-facing product data, not JSON-first diagnostics.
Open review in a wide, scroll-contained confirmation surface and present live
and proposed values side by side using the visual structure and terminology of
the source module's editor. Label the two states explicitly as `CURRENT LIVE /
BEFORE` and `PROPOSED / AFTER`; for additions, replace the empty live panel with
an explicit `NEW RECORD` state. Stack comparisons on narrow screens. Raw stored
payloads remain available in a collapsed technical-evidence disclosure for
troubleshooting and audit support, but must not be the primary review surface.
Each module should add a purpose-built preview as its editor matures; until then,
use compact labeled field cards rather than an exposed JSON document.
Data-navigation links with pending proposals use a dedicated four-column layout:
module code, flexible label, square orange count cell, then navigation arrow.
Never insert a count into the ordinary three-column link without changing its
grid, because the arrow will wrap to another row. The count includes textual
assistive labeling and strengthens its border and contrast on link hover or
active state.
The same compact count cell identifies actionable work in System Oversight
navigation. Show it only for queues with unresolved items, such as pending data
proposals or avatar reviews, and calculate it within the viewer's permissions.
Do not add zero badges or counts to informational registries such as Audit
History and System Metrics. Badge text must expose an `awaiting review` label to
assistive technology; color supplements that meaning rather than replacing it.
Registry name search is server-backed and updates as the user types after a
short debounce; it must search the complete filtered registry rather than only
the visible page. Preserve active filters and row-count settings, reset results
to page one, keep input focus, avoid scrolling, and replace browser history so
each keystroke does not create a navigation entry. Retain an explicit Search
button for immediate submission and keyboard/accessibility fallback. During
navigation, expose a textual pending state and suppress duplicate submissions.
Builder option grids support an icon-only square mode for small, icon-led
catalogs such as a gear item's two-to-four compatible Brand Sets. In this mode,
the compact 58-pixel cells reset the ordinary text-and-icon grid to a single
centered track, and the transparent icon fills that square. Align the fieldset
with adjacent compact controls rather than inheriting the standard radio-grid
top margin. Visible truncated names are omitted. Preserve the complete name as
the radio control's accessible label and
as hover text. Selection, hover, pressed, and keyboard-focus states remain on
the square perimeter; missing icons fall back to the entity initials.
Builder level controls enforce item availability without changing the scaling
curve's origin. T0 and T1 gear and weapons allow levels 1–50; T2 gear and
weapons allow levels 40–50 because lower-level T2 items are not found in game.
Their base-value progression still follows the same level-1-based curve.
Selecting T2 on a weapon below level 40 raises the configured level to 40 and
calculates the ordinary level-40 curve value; never rebase level 40 as T2 level
1. Slider bounds, direct-input validation, displayed bounds, and clamping must
all enforce the same availability minimum.
ISAC ranges that represent observed values retain their explicit reference
level. The progression curve still has a level-1 origin, but builder scaling
must calculate the ratio between the target level and the stored observation's
`reference_level`. Do not treat every observed endpoint as a level-1 value
merely because the curve begins there.
Weapon damage is the exception to reference-observation scaling: each
weapon-tier damage range is stored as a hidden base and must be multiplied
directly by `weaponMultiplier(selectedLevel)`. Changing weapon level remaps the
selected damage value into the newly calculated range. Shared core attributes
remain level-50 reference profiles and use ratio scaling independently.
Gear base Health and Armor follow the same hidden-base principle. Multiply
their stored per-item endpoints directly by `gearMultiplier(selectedLevel)`,
truncate only for display, and remap the user's selected value proportionally
when gear level changes. Do not apply this rule to rolled Attributes or the
shared core-allocation profile, which retain independent reference semantics.
### Shared core allocation
Firepower, Toughness, and Engineering use one system-wide tier profile shared
by gear and weapons. Maintain the T0/T1/T2 nominal ranges in ISAC Global
Configuration rather than repeating them on every item editor. The initial
level-50 profiles are T0 `30–78`, T1 `36–94`, and T2 `44–112`. Global
configuration mutations follow ordinary contributor moderation and append-only
ISAC audit rules.
The three nominal ranges define a combined allocation pool, not three hard
per-stat clamps. At a given level, the valid total lies between three times the
scaled nominal minimum and three times the scaled nominal maximum. An
individual core may be zero or exceed the nominal maximum when points are
redistributed, up to the entire pool maximum. Builder gear and weapon editors
show three sliders/direct inputs plus an explicit current-total versus
valid-pool readout; out-of-pool totals use textual red/restricted feedback.
Both equipment families contribute their configured cores to the build summary.
Treat this redistribution model as empirical and keep it configurable as more
in-game edge cases are observed.
Pending records may be used as dependencies in later proposals by any
contributor. Render these choices in the ordinary assignment control with a
restrained orange border/background and the explicit text `PENDING` plus the
proposer’s display name; never present them as live data. Approval is dependency
ordered: a dependent proposal stays pending until every referenced proposal is
approved, while rejected or failed dependencies block it. This state must be
conveyed by text as well as color and must remain legible in compact and
responsive editor layouts.
Large many-to-many selections use the shared searchable assignment control,
including weapon talent pools, gear talent compatibility, and compatible brand
sets. The control keeps assigned records visible as removable chips, filters
available records by name, limits the rendered result set, and submits canonical
hidden inputs. Configure its subject labels and empty-state copy for the entity
being assigned rather than embedding talent-specific language in the shared
component. Pending contributor records retain their orange treatment and named
proposer in both search results and dependency workflows.
Audit History may use a five-column KPI filter rail when all durable operational
domains need first-class visibility. It retains the standard compact KPI height
and collapses through the shared dashboard breakpoints; do not increase card
height or create a second row solely to accommodate the fifth domain.
Inherent permissions that cannot be removed through role or user assignment use
the compact blue information rail above the affected access controls. Always
name the baseline and enumerate its capabilities so operators do not mistake an
empty role assignment for an account with no effective access.
### Mobile registry detail targets
On narrow screens, a non-editing registry row with one canonical detail or
review destination becomes a full-row tap target. Use a real stretched link
when the destination is another route and a transparent button when it opens an
in-page detail surface. Keep the explicit desktop `VIEW`, `REVIEW`, or
`EVIDENCE` action, and add a visible mobile cue at the bottom of the row so the
interaction is discoverable.
The target must expose an accurate accessible name, show the standard orange
focus-visible outline, provide pressed feedback, and meet a minimum 48-pixel
tap height. Do not apply this pattern to edit, delete, approve, or other
mutating actions, and do not stretch a target across rows containing independent
interactive controls.
### Avatar Oversight
Custom avatars are active immediately and enter a separate Avatar Oversight
queue. The registry uses compact image-evidence cards rather than a text table:
portrait, explicit lifecycle state, agent identity, submission time, normalized
size, reviewer, and note stay visible together. Pending is orange, approved is
green, and removed is red; withdrawn and superseded remain neutral. State text
is mandatory.
Review uses the standard ISAC modal and two equal decision controls. Approval
records the review without changing the active image. Removal requires a reason
and explicit confirmation, deletes the custom file, and returns the account to
the default SHD portrait. A stale review must never act on a newer upload.
Historical entries retain metadata and audit evidence even when their image is
no longer available. At narrow widths cards become one column, retain a large
portrait preview, and expose a minimum 44-pixel review action.
### Notification network
Authenticated navigation exposes a compact inbox glyph with a numeric unread
badge; mobile navigation provides the same explicit `INBOX` destination and
count. Notifications live under the agent-profile namespace and use an explicit
profile context trail back to account controls. The inbox is a dense
communication registry with a source rail, category,
title, timestamp, body, destination, and textual read state. Unread messages use
the orange action rail. Mandatory ISAC notices use the blue operational rail and
must always remain visible in the in-app inbox.
Preferences are channel-by-category rather than global. Each category controls
in-app, browser, and email delivery independently. ISAC in-app delivery is
locked on, while its browser and email channels remain optional. Email defaults
to off and offers `OFF`, `IMMEDIATE`, and `DAILY DIGEST`; browser delivery must
follow an explicit browser permission gesture. Never imply background push when
only active-site browser polling is available. At narrow widths preference rows
stack their identity above channel controls and notification entries collapse
their source rail above the message.
The main Agent Profile surfaces the five most recent enabled inbox messages
before authored-content modules and includes unread count as a profile metric.
This preview preserves mandatory, unread, source, and category states, uses
single-line truncation at desktop density, and links to either the notification
destination or full profile inbox. On mobile, metadata becomes its own row and
message body copy may wrap.
Inbox messages use sender identity and semantic tone. ISAC sources use the SHD
system mark, blue system labeling, and an explicit `SYSTEM` descriptor;
community activity uses an agent portrait and `AGENT` descriptor. Positive
outcomes use green, adverse or corrective outcomes use red, and informational
content stays neutral. Message titles and body copy state the actual outcome;
do not expose internal sentiment labels such as `POSITIVE`, `NEGATIVE`, or
`NEUTRAL`. Reserve visible badges for operationally meaningful severity such as
`WARNING` or `ERROR`, while the message wording remains understandable without
depending on color alone.
The inbox command header pairs the page identity with a compact bordered unread
summary and a same-height preferences action. Filter selects and adjacent
actions share a 40-pixel control height and vertically centered labels. Within
each message, the tone/title/timestamp header uses modest vertical padding and a
subtle lower rule: keep it dense, but never collapse it into a text-height strip.
On mobile the unread summary and preferences action share one grid row, while
filter controls stack into full-width, minimum 44-pixel targets.
Page hierarchy uses the shared eyebrow breadcrumb rather than a separate
breadcrumb row above a command header. It retains the short orange rule, renders
ancestors as orange links, separates levels with a muted slash, and marks the
final item as the current page. A top-level page may use a single current item.
Do not duplicate the same hierarchy in both an eyebrow and a second contextual
navigation strip; this creates visual overlap and wastes vertical space. The
breadcrumb always uses intrinsic height and must not inherit the full-height
primary-navigation treatment. Likewise, in-page command and message headings
must use their named component classes rather than the global application
`header` element treatment.
`VIEW DETAILS` opens the standard in-page modal rather than navigating
immediately. The modal repeats sender, source, category, semantic tone, time,
full message body, read control, and optional related-page destination. This
keeps review context in the inbox and makes outbound navigation deliberate.
ISAC message content uses the structured system-transmission body. A compact
blue `ISAC TRANSMISSION` kicker precedes the summary, and recognized supporting
fields such as `OVERSIGHT REASON`, `REQUIRED ACTION`, or `DETAILS` become a
labeled detail row beneath a subtle divider. The expanded message view uses the
same hierarchy at reading size. Community messages remain conversational body
copy; do not apply the system-transmission frame to agent comments.
Inbox entries expose two distinct actions. `VIEW DETAILS` opens the complete
message when preview copy is truncated to two lines. A separate bordered orange
response action navigates directly to the known workflow and uses specific
language such as `UPLOAD NEW AVATAR`, `REVISE CONTRIBUTION`, or
`VIEW BUILD & REPLY`; never substitute a generic destination label when the
notification type is known. The same response label appears in the detail
modal. On mobile, response actions become full-width 44-pixel targets.
Inbox filtering uses additive segmented selectors derived from the OS Protocol
quality control. Channels and message states are independent checkbox-backed
segments, all selected by default, and each selection change applies
immediately without a submit button. Selected segments use the orange lower
rail and filled square indicator; deselected segments remain readable and
available. The channel rail collapses from six to three and then two columns as
space narrows, with minimum 44-pixel mobile targets.
Use the shared `SegmentedChoice` component for every static, scannable ISAC
choice set containing two through six options. Radio mode represents one
required choice; checkbox mode represents additive independent choices. This
rule applies equally to editors and registry filters: do not introduce a native
dropdown merely to save vertical space. Use the shared `SearchableAssignment`
component in single or multiple mode when there are seven or more options, when
the options come from a growing data registry, or when search materially helps
recognition. Compact mode is the standard treatment for filter bars and dense
repeated rows; full mode is used for primary relationships.
Native selects are not a default ISAC control. Retain one only when a documented
browser-native behavior cannot be represented accessibly by either shared
component. Such an exception must be called out in this design system in the
same change. Both shared choice patterns expose explicit selection, hover,
pressed, focus-visible, disabled, empty/all, responsive, and 44-pixel
mobile-target states so modules do not reimplement them.
Numeric inputs inside ISAC use overwrite-first focus behavior: focusing an
enabled `type="number"` control selects its complete current value so a
contributor can immediately type a replacement instead of appending to a
default zero. This behavior belongs to the shared ISAC layout boundary and must
not be reimplemented in individual modules. Disabled numeric controls remain
unfocusable and unchanged.
`SegmentedChoice` supports two restrained density variants. Icon-only segments
may omit visible text when a canonical, unambiguous game icon exists; they must
retain an accessible label and hover title. Text-only segments may omit the
default square indicator when the selected background, orange text, and lower
rail already communicate state clearly. Do not remove both visible text and an
icon from the same option.
### Skill Chip data console
Skill Chip editing follows the dense ISAC staged-definition pattern: identity
and icon first, the 2-piece numeric bonus second, mutually exclusive primary
and secondary roll pools next, and the 3-piece talent last. Superior blue and
High-end yellow communicate the only supported qualities; orange remains
reserved for actions and current interactive state. A four-step segmented
preview exposes the linear zero-to-three High-end interpolation without storing
duplicate values. At narrow widths the roll pools and talent columns stack,
preview segments become a two-by-two grid, and interactive targets remain at
least 44 pixels tall.
### ISAC record identity controls
Game-data icon editors use the shared `--isac-icon-editor-width` footprint
(150 pixels on desktop) regardless of module. Identity layouts allocate that
fixed column to `IconUploadField`, give the record name the flexible dominant
column, and stack both controls at mobile widths. Do not enlarge an upload
drop-zone to fill spare identity-row space.
Icon-enabled consecutive-entry editors pass a successful-add result token into
the shared upload control. Advancing that token clears the native file input,
temporary preview URL, filename, drag/paste feedback, and validation state.
This reset occurs only after a validated add succeeds; failed saves preserve
the selected image, and successful edits close the editor normally.
The primary `name` field in every ISAC data editor is a header-level input:
46 pixels tall with larger, heavier text. This creates a consistent first
reading point for long registries while keeping secondary metadata compact.
`SearchableAssignment` is the standard relationship picker for long attribute,
talent, trigger, and compatibility lists. Multiple mode manages additive sets;
single mode presents the same searchable selected/result treatment for one
relationship. Prefer these modes over native selects once a source registry is
large enough that scanning a dropdown is inefficient. The selected record,
pending-contribution state, keyboard focus, and remove interaction must remain
explicit in both modes.
Every ISAC relationship to an Attribute uses `SearchableAssignment`; native
attribute dropdowns are not permitted because the registry is expected to
contain hundreds of records. Use full mode for primary editor sections and
multi-value pools. Use compact single mode inside repeated effect, bonus, or
augmentation rows: its selected record and search field remain inline while
the filtered result registry opens as an anchored overlay. The compact overlay
must layer above adjacent rows, retain pending-record labels, and collapse to
one column on narrow screens.
Gear identity editing uses the shared segmented radio treatment for both Gear
slot and item tier. Show all six slots together and all three tiers together so
contributors can verify classification without opening a menu. The record name
remains the dominant text field; at medium widths it occupies a full row above
the two selectors. On narrow screens the slot selector becomes a three-by-two
grid, both selectors use at least 44-pixel targets, and changing slot clears
incompatible Talent relationships exactly as the former selector did. Gear
slot segments use icon-only controls with accessible slot names; registry labels
retain icon and text for rapid table scanning. Tier segments use the compact
game notation `T0`, `T1`, and `T2`, omit the redundant square indicator, and
rely on the selected rail and color; do not expand these to “Tier 0” inside
dense segmented controls. On desktop, icon-only Gear slot segments are square
32-pixel cells so their classification rail stays compact and the adjacent
record-name input receives the dominant width. Increase those cells to square
44-pixel targets at mobile sizes without reintroducing visible slot text.
Weapon-category editing follows the same compact icon-rail treatment: Shotgun,
Rifle, MMR, AR, LMG, and SMG use their canonical weapon silhouettes in square
32-pixel desktop cells and 44-pixel mobile cells, with accessible names and
hover titles retained. Exotic remains an independent icon-backed status because
an Exotic weapon must also retain its underlying weapon category. Weapon
Damage Type follows the same icon-only treatment when canonical Blast, Pierce,
and Shredding marks are available. Exotic status is a single square checkbox
whose icon, orange rail, and background show its checked state; no visible
“Exotic weapon” label is repeated inside the control. Weapon registry rows show
both the category silhouette and, when applicable, the Exotic mark alongside
the textual class for rapid scanning.
Weapon Tier Progression begins at the selected Native Tier. Lower tier cards
remain visible for orientation but are disabled, desaturated, and explicitly
labeled unavailable; their values are neither required nor persisted. Each
available tier has one Damage range labeled with the selected damage-type icon
and one shared Core range for Firepower, Toughness, and Engineering, represented
by all three primary-attribute icons. Registry completeness compares configured
tiers with the number possible from the weapon’s Native Tier. Archive Weapon
uses the standard separated ISAC destructive-action control on the left side of
the editor action band.
Root-level static PNGs are interface icons and use content-fitted transparent
bounds. Run `npm run assets:trim-icons` after adding or replacing them. The
normalizer trims fully transparent padding, restores a two-pixel transparent
safety margin so antialiased edges never touch the canvas, preserves RGBA PNG
output, and leaves portrait/avatar and SVG asset classes untouched.
### Shared equipment profiles
Configuration inherited by every item of a finite equipment type belongs in a
collapsible shared-profile registry above the item table. Each profile card
states its type, required slot count, configured state, and a compact summary;
selecting it opens a separate dense editor and closes the individual-record
editor. Successful saves close the profile editor and return to the registry.
Gear Tactical Augment profiles use this pattern for all six gear types.
Backpacks and Body Armor expose four required slots; Holsters, Gloves, Knee
Pads, and Masks expose three. Augment 01 contains one searchable Attribute,
one numeric effect value, and an unlock cost. Later slots expose only their
unlock cost because their effects are fixed base-item upgrades: three-slot
profiles use +50% and +100%, while four-slot profiles use +40%, +70%, and
+100%. Holsters, Masks, and Backpacks modify the equipped item's base Health;
Gloves, Knee Pads, and Body Armor modify that item's base Health and Armor.
These percentages never apply to the overall character total. Every augment
slot stores separate non-negative whole-number unlock costs for item Tiers 0,
1, and 2. Show all three costs together in registry summaries and in a compact
three-column cost fieldset within the editor; the builder shows only the cost
matching the equipped item's tier. Fixed upgrades
use a green inherited-value panel to distinguish governed data from editable
orange actions. At narrow widths profile cards and augment editor cards reduce
to a single column while retaining 44-pixel interactive targets.
### Loadout workbench
The builder is a three-rail tactical workbench: compact equipment positions on
the left, the currently selected item editor in the center, and a sticky live
calculation summary on the right. At medium widths the summary becomes a
full-width lower rail; on mobile all three rails stack, with equipment first,
editing second, and results third. Equipment positions remain explicit even
when empty so loadout completeness is always visible.
Use the shared `BuilderPicker` for builder catalogs and Attribute pools. It
combines an always-visible current selection with a searchable overlay, icon or
tactical fallback mark, classification metadata, selected state, and conflict
state. Conflicting catalog entries remain visible but disabled so restrictions
such as the single-exotic-weapon rule are explained rather than hidden.
Numeric instance values display their authoritative minimum and maximum beside
the field and clamp to that range on completion. Always-on item rolls, fixed
bonuses, inherited profiles, enabled augments, and activated set bonuses feed
the live totals immediately. Trigger-dependent talents remain explicitly
`CONDITIONAL` until the user enables `TRIGGER ACTIVE`; only then may their
effects enter combined totals. This textual distinction must remain clear
without relying on color.
### Pending ISAC registry records
Pending additions appear directly in the same paginated ISAC registry as live
records and in every compatible relationship picker. They use a restrained
orange-tinted row, an explicit `PENDING` state, and proposer attribution.
Pending rows are read-only in module registries; changes remain the authority
of the Data Oversight review interface. Successful contributor submissions
refresh the registry immediately and follow the same editor lifecycle as a
direct save. Never rely on the tint alone to communicate moderation state.
Gear and weapon level are builder-instance state, never part of an ISAC item
definition. Each equipped item stores its own level from 1–50 in the local or
persisted build. Range endpoints are calculated from the documented level
curve and stored reference level; provisional portions of that curve must be
identified in text as estimated.
### Registry relationship icon rails
When a table relationship contains a small bounded set of visually identified
records, show a compact icon rail instead of a numeric relationship count.
Rails use 30-pixel square cells with four-pixel spacing, uploaded transparent
icons, full accessible names, and native hover titles. Missing artwork falls
back to a short orange text mark rather than removing the relationship.
Use this pattern only for small bounded sets that remain scannable, such as the
two-to-four compatible Brand Sets on Gear. Long or unbounded relationships
continue to use counts, searchable inspection, or expanded detail views.
Public Gear cards use the same rail for compatible Brand Sets and available
Talents so players can identify actual options instead of reading a count.
Each icon retains its accessible name and hover title, and missing artwork uses
the shared placeholder without suppressing the relationship. Relationship
icons are buttons: activating one opens a modal quick view using the same card
facts as that record's public database category. The overlay closes through
its explicit close action, backdrop, or Escape key. Do not render an empty
Talent rail on item types that cannot equip Talents.
### Public game-database registries
The public Database is a player-facing reference surface, separate from the
ISAC editing workflow. Its index exposes eight durable categories: Gear,
Weapons, Weapon Mods, Gear Mods, OS Protocols, Talents, Brands, and Skill
Chips. Attributes are implementation vocabulary resolved into the facts and
effects shown on those records; do not promote Attributes to a public
top-level category.
Each index category is a fully interactive square registry card with a live
approved-record count, concise scope statement, visible hover/focus state, and
pressed feedback. A category page uses the shared compact category rail,
live name search, pagination, and a responsive card grid. Record cards use the
same hierarchy:
- a fixed icon cell and compact identity header;
- small textual badges for quality, tier, or exceptional state;
- bordered fact cells for primary values;
- relationship lines and tier descriptions only when applicable.
When a record represents an ordered progression rather than independent facts,
use the stacked fact treatment. Brand Set bonuses are shown vertically in
two-, three-, then four-piece order; do not add a redundant “3/3 bonuses”
badge when all three rows are already visible.
Weapon cards also use stacked facts. Their level-50 Damage range is the primary
fact: a taller orange-railed row with enlarged values and the Damage Type in
the label. Public damage is calculated from the stored level-1 endpoints using
the same `weaponBaseRange` curve as the builder; never label the configured
base endpoints as level-50 values. Weapon Talent 1 and Talent 2 pools use
separate clickable icon rails and the standard Talent quick view.
Record cards may end with contextual actions that connect related public
registries. Actions use the compact orange tactical-button treatment and
encode their destination filter in the URL. Brand cards provide a “find Gear”
action that opens the Gear registry with that Brand selected; the same action
must remain available in Brand quick views.
Cards display canonical active data only. Attribute references must be
resolved to readable names, values, and units instead of exposing IDs or raw
JSON. Missing uploaded art uses the standard missing-item placeholder.
Desktop layouts use three record columns, intermediate layouts use two, and
narrow layouts use one. The eight-category rail reduces from eight to four to
two columns. Preserve keyboard focus, pressed feedback, reduced-motion
behavior, and readable textual state in every layout.
Category-specific discovery uses the shared tactical filter matrix. Filters
apply immediately, remain encoded in the URL, reset pagination, and compose
with name search. Multiple selections inside one facet are inclusive (`OR`);
active facet groups combine restrictively (`AND`). For example, selecting two
Brands and four Talents returns Gear matching any selected Brand and any
selected Talent. Never implement multi-select facets as an implicit match-all
within the same group.
Use segmented icon choices for bounded visual vocabularies of six or fewer,
such as Gear slots. Use the matching searchable multi-select list for growing
sets such as Brands and Talents. Always expose selected counts, a single clear
action, `aria-pressed` state, keyboard focus, and explicit text explaining the
combination rule. At intermediate width, the bounded facet spans the row above
two searchable facets; on narrow screens all facet groups stack vertically.
Weapon discovery applies this pattern to weapon-family icons, damage-type
icons, and the searchable pool of compatible Talents. Talent facet rows show
the rendered Tier I description beneath the name. Talent searches match both
fields case-insensitively so mechanical phrases can discover talents even when
the talent name is unknown. Brand facets similarly index the names of all
set-bonus Attributes, allowing players to search by the desired mechanic
without exposing that metadata as extra visual clutter.
Public database command headers are content-sized and must explicitly override
the global 76-pixel site-header rule. Their eyebrow, title, description, count,
and index action remain inside the bordered command surface at every scale.
Children use `min-width: 0`, long titles may wrap, and category navigation
tracks use zero-minimum grid columns so translated or enlarged text cannot
force horizontal page overflow.
### Build persistence and sharing
The builder distinguishes device persistence from account persistence. `SAVE
LOCALLY` writes a working draft to the current browser. Signed-in agents also
receive one account-save action paired with an explicit visibility selector:
- `PRIVATE` is owner-only.
- `UNLISTED` is accessible by direct link but excluded from discovery.
- `PUBLIC` is accessible by direct link and included in the public archive.
Keep visibility adjacent to the account-save action and explain its selected
state in plain language. Show a share-page action only after a non-private
build has been saved. Public and unlisted build pages are read-only tactical
summaries; owner editing returns to the builder. On narrow layouts,
persistence controls and shared-loadout columns stack vertically.
### Tiered compatibility editors
When compatibility depends on two independent dimensions, keep those
dimensions explicit rather than presenting a flattened list. Weapon
definitions configure their available Weapon Mod slots per tier. T0 requires
four distinct slots; T1 and T2 require five. Weapon Mod definitions then
configure one slot plus one or more compatible weapon categories. The UI and
validation must require both dimensions to match.
Weapon Mod definitions use the shared segmented-choice control for the bounded
eight-slot vocabulary, with a full-width row on desktop and two columns on
narrow screens. Weapon category compatibility uses the same control in
checkbox mode with weapon icons and retained accessible names. Show configured
versus required slot counts in text. Disabled tiers remain visibly unavailable
and must not submit slot assignments.
Quality-scaled registry editors use a compact matrix when the same numeric
shape repeats across all four quality levels. Apply global quality colors only
to the quality labels, retain textual quality names, and keep numeric fields
neutral. On narrow screens the matrix may scroll horizontally while its
surrounding editor and previews stack.
When two item slots draw from the same canonical option pool, edit that pool
once rather than rendering two duplicated selectors. Weapon Mods use a
repeatable secondary-attribute row: one searchable attribute reference plus
four quality range pairs. Show that the builder selects two distinct options
from the pool. Primary core type is likewise builder-selected and is displayed
as the three always-available core icons rather than a persisted ISAC choice.
Exotic Weapon Mod status is a separate single-icon toggle using the exotic
icon and the standard pressed, focus-visible, and selected states. Selecting
it must switch the editor immediately, before a weapon is assigned. Exotic is
its own unique quality: reveal only the required exotic-weapon assignment,
remove weapon-category and regular-quality controls, and replace random ranges
with fixed level-1 and level-20 attribute values. Keep an explicit Exotic
Quality state panel visible so the change in data semantics is unmistakable.
### Gear Mod registry
Gear Mods reuse the compact quality matrix, level preview, and pooled
secondary-attribute rows established by Weapon Mods. Do not render exotic,
weapon-category, slot-type, or Mod Effect controls in this simpler editor.
Present the five primary options as an always-visible informational band
because the actual primary is selected on the build instance, not persisted on
the canonical definition.
Use explicit textual tier capacity wherever Gear tier is summarized: `1 MOD
SLOT` for T0 and `2 MOD SLOTS` for T1/T2. On narrow screens the identity band
and preview controls stack, while repeated quality data may scroll rather than
compressing numeric controls below usable sizes.
### Deferred game-data icons
Game-data icons are optional enrichment, never a blocker for creating or
proposing a canonical item. Upload fields must state that an icon may be
contributed later and show the shared `/missing-item-icon.svg` artwork when no
upload exists. Registries, relationship rails, and builder selection controls
use the same placeholder instead of initials, empty boxes, or ad hoc glyphs.
Treat the placeholder as an explicit incomplete-data state: keep its subdued
SHD styling and useful alternative text where the surrounding item name is not
already available. Do not persist placeholder metadata or copy the placeholder
into per-item storage. A later contributor replaces it through the normal icon
upload proposal, preserving the canonical record and audit history. The upload
itself retains all transparent-format, size, SVG-safety, focus, drag/drop, and
paste validation requirements.
### Public database performance facts
Use the shared `DatabaseFacts` renderer for registry-card performance data and
quick views. Weapon identity headers keep the native tier beside the category
identity as a compact textual `T0`, `T1`, or `T2` marker. Place tier and the
smaller damage-type icon in a dedicated top-right status cluster; do not
interrupt the readable `category // damage type` metadata with icons. Reserve
the emphasized first fact for the weapon's damage range, pair it with the
canonical damage-type icon, and render the calculated level as a subdued
annotation rather than part of the primary label. Public level-50 damage must
be calculated from the stored level-1 endpoints with `weaponBaseRange`; never
relabel raw source values.
Represent bounded handling values such as Accuracy and Stability with compact
horizontal meters. Always retain the numeric value and an accessible
`value out of 100` label so the graphic does not carry meaning alone. Use the
stable sequence `label → meter → exact value`, with the exact number aligned in
a dedicated column to the right of the graphic. Clamp display values to
`0–100`, use orange only for the filled measure, and keep the unfilled track
neutral. Stack meter labels and tracks on narrow cards rather than shrinking
them below a reliably readable width.
Talent registry cards treat the two tier descriptions as their primary
content; never replace them with configured/missing summary facts. Render Tier
I and Tier II as distinct structured data blocks, then place optional cooldown
metadata below both descriptions. Do not use the serif, quote-like prose
treatment for tier effects. Use compact fixed tier labels, sans-serif effect
text, and engineered borders. Tier I is the neutral baseline; Tier II uses the
orange rail, label, and subtly elevated surface to communicate that it is the
improved version. Duration and stack limits may remain compact badges. The
top-right header status uses the applicable Backpack or Body Armor icon for
gear talents and the SMG icon as the generic Weapon Talent symbol. Weapon
Talent headers pair that icon with an explicit `1 · TOP` or `2 · BOTTOM`
status badge; never rely on the slot number alone.
When a public registry needs only one small bounded filter vocabulary, use the
compact facet-panel form: one full-width group with all options visible in a
single row on wide screens. Talent slot filtering uses Backpack, Body Armor,
Weapon 1 · Top, and Weapon 2 · Bottom. Always include the Top/Bottom context
when presenting numbered Weapon Talent slots to users. The filter remains
additive within the group and wraps to two columns on narrow screens; do not
introduce search or dropdown controls for this four-option set.
Notification delivery preferences are a temporary profile control surface.
After a successful save, close the interface and return focus to the inbox
context. Consume a unique server result token only once so later actions or
reopening the panel do not close it again. Validation and server errors keep
the interface open with the submitted controls available for correction.
### Global toast feedback
Use the root-mounted `ToastViewport` and the shared API in `src/lib/toast.ts`
for transient action feedback. Enhanced server mutations should normally use
`PendingForm`, which derives success and failure feedback from the action
result. Client-only operations call `toast.success`, `toast.error`,
`toast.warning`, or `toast.info` directly. Do not create page-specific toast
implementations.
Toasts occupy the top-right safe area below the global header and show no more
than four messages. Each uses a textual state label, tone-colored top and left
rails, a concise title, optional supporting text, and an explicit dismiss
control. Success is green, errors use danger red, warnings use SHD orange, and
informational feedback uses restrained blue-gray. Color never carries state
alone.
Use success to confirm completed mutations, error for failed requests, warning
when attention is required, and info for neutral client or system state. Keep
inline validation beside affected fields even when an error toast is also
emitted. Avoid toasts for passive navigation, filtering, hover, unpersisted
selection changes, and background refreshes. Toasts automatically dismiss
according to severity, remain keyboard dismissible, use `status` or `alert`
semantics, and disable entrance motion with reduced motion. On mobile they span
the safe viewport width below the header without obscuring navigation.
### ISAC progressive disclosure
The ISAC rail shows every module the current operator is authorized to access,
but groups modules behind collapsible operational categories. The category
containing the current route opens on first use; subsequent open and closed
choices are remembered locally for that browser. Searching the rail temporarily
reveals matching modules across all authorized categories without changing the
remembered category state.
Use the compact command-status strip for persistent system, access, environment,
and data-mode context. These values remain textual and must not rely on color
alone. Registry search remains available for a single page of results, while
page navigation and row-count controls appear only when the result set spans
multiple pages. This keeps expert lookup fast without presenting inactive
controls to contributors and first-time Oversight operators.
Sidebar category headers are real buttons with `aria-expanded` and controlled
navigation regions. Search fields have visible or accessible labels, keyboard
focus uses Signal Orange, and collapsed state must remain understandable
without motion.
### ISAC mutation consequence
Every writable game-data module states what Save will do before the operator
opens an editor, and the statement remains visible while the editor is open.
The message is derived from effective permissions rather than copied into each
module:
- `OVERSIGHT REVIEW` means saving creates a proposal and does not change live
data until Oversight approves it.
- `DIRECT PUBLISH` means the change becomes live immediately and creates an
append-only audit event.
Pair the short state label with a complete consequence sentence. Contributors
must not have to infer this behavior from their role name, data-mode color, or a
success message shown after saving. Read-only operators do not see a mutation
statement because their mutation controls are absent. At narrow widths the
message becomes its own compact row above the module toolbar.