1475 lines
88 KiB
Markdown
1475 lines
88 KiB
Markdown
# 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` |
|
||
| 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
|
||
|
||
- The current UI stack is `Arial, Helvetica, sans-serif`.
|
||
- Page and module headings are uppercase, heavy, and tightly spaced.
|
||
- Eyebrows and table labels use 7–10px text with 1–2px letter spacing.
|
||
- Body and form text should normally remain between 9–12px in dashboards.
|
||
- Public/editorial copy may use Georgia for a restrained field-manual character.
|
||
- 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.
|
||
- 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.
|