Update styling across app

This commit is contained in:
JB
2026-07-24 21:41:01 -04:00
parent 5e113454ee
commit 8a0bfd2187
9 changed files with 109 additions and 19 deletions
+35
View File
@@ -0,0 +1,35 @@
# Repository Instructions
## SHD design-system maintenance
Treat `docs/DESIGN_SYSTEM.md` and the shared tokens and patterns in `src/app.css`
as maintained parts of the product.
Whenever a change makes a significant visual or interaction-design decision,
update the design system in the same change. This includes:
- introducing or changing colors, typography, spacing, borders, elevation, or motion;
- creating a new reusable layout, navigation, table, editor, form, card, status, or feedback pattern;
- materially changing dashboard density, hierarchy, responsive behavior, or accessibility behavior;
- changing the semantic meaning of an existing visual state;
- establishing a pattern that future pages or components should follow.
For significant design changes:
1. Reuse or extend the CSS custom properties in `src/app.css`; do not scatter
near-duplicate hard-coded values throughout new components.
2. Update `docs/DESIGN_SYSTEM.md` with the new or revised rule, token, pattern,
usage guidance, and responsive or accessibility considerations.
3. Refactor repeated UI into a reusable component when that makes the pattern
clearer and reduces divergence.
4. Preserve the SHD design principles: tactical but restrained, data-first,
square and engineered, orange reserved for action and emphasis, and explicit
textual state indicators.
5. Verify hover, pressed, focus-visible, pending, disabled, error, success, and
reduced-motion behavior where applicable.
6. Run `npm run check` and `npm run build` before completing the change.
Minor alignment fixes, isolated bug fixes, and corrections that do not introduce
or revise a reusable design decision do not require a documentation update.
If uncertain whether a visual change is significant, update the design system.
+30
View File
@@ -45,6 +45,22 @@ Use the spacing tokens for new components. Prefer `8px`, `12px`, and `20px` gaps
- 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.
- 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.
- On small screens, the split frame becomes a single column and account controls
move before archival content.
### Admin dashboard
@@ -98,3 +114,17 @@ Do not communicate state by color alone; include a label, icon, or pattern.
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`.
+8
View File
File diff suppressed because one or more lines are too long
+4 -3
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { page } from '$app/state';
import PendingForm from '$lib/components/PendingForm.svelte';
import type { Snippet } from 'svelte';
let { user, children }: {
@@ -16,7 +17,7 @@
<header class="site-header">
<a class="brand" href="/" aria-label="SHD.build home">
<span class="brand-mark">SHD</span><span>SHD<b>.build</b></span>
<span class="brand-mark"><i></i><b>SHD</b></span><span class="brand-name">SHD<span>.build</span><small>LOADOUT NETWORK</small></span>
</a>
<nav aria-label="Main navigation">
{#each links as link}
@@ -31,8 +32,8 @@
</nav>
{#if user}
<div class="account">
<a href="/account"><small>AGENT</small>{user.username}</a>
<form method="POST" action="/sign-in?/logout"><button>SIGN OUT →</button></form>
<a href="/account"><small><i></i> AGENT ONLINE</small>{user.username}</a>
<PendingForm action="/sign-in?/logout">{#snippet children(pending)}<button aria-busy={pending}>SIGN OUT →</button>{/snippet}</PendingForm>
</div>
{:else}
<a class="sign-in" href="/sign-in">SIGN IN →</a>
+23 -7
View File
@@ -1,8 +1,24 @@
<script lang="ts">let { data, form } = $props(); const user = $derived(data.user!);</script>
<script lang="ts">
import PendingForm from '$lib/components/PendingForm.svelte';
let { data, form } = $props();
const user = $derived(data.user!);
</script>
<svelte:head><title>{user.username} — SHD.build</title></svelte:head>
<main class="page"><div class="page-head"><div><p class="eyebrow">AGENT PROFILE</p><h1>{user.username}</h1><span class="accent">{user.role.toUpperCase()}</span></div><div><b>{data.account.builds.length}</b> builds · <b>{data.account.guides.length}</b> guides · <b>{data.account.favorites.length}</b> favorites</div></div>
{#if data.chooseUsername}<p class="notice">Choose the unique SHD.build username other agents will see.</p>{/if}{#if data.discordLinked}<p class="success">Discord account linked successfully.</p>{/if}{#if data.oauthError}<p class="error">Discord linking could not be completed.</p>{/if}
<div class="grid" style="grid-template-columns:1fr 380px">
<section class="grid"><div class="panel"><div class="page-head"><div><p class="eyebrow">LOADOUT ARCHIVE</p><h2>My builds</h2></div><a class="secondary" href="/builder">NEW BUILD +</a></div>{#if data.account.builds.length}{#each data.account.builds as build}<div class="table-row"><b>{build.title}</b><span>{build.visibility}</span><span>{new Date(String(build.updated_at)).toLocaleDateString()}</span><span></span><button>OPEN</button></div>{/each}{:else}<p class="muted">Your saved and published loadouts will appear here.</p>{/if}</div><div class="panel"><h2>Favorites and ratings</h2><p class="muted">{data.account.favorites.length} favorited · {data.account.ratings.length} rated</p></div></section>
<aside class="panel"><p class="eyebrow">ACCOUNT SETTINGS</p><h2>Username</h2><form method="POST" action="?/changeUsername"><label class="field">PUBLIC USERNAME<input name="username" value={user.username} required /></label>{#if form?.usernameError}<p class="error">{form.usernameError}</p>{/if}<div class="form-actions"><button class="primary">UPDATE</button></div></form><hr /><h2>Sign-in methods</h2><a class="secondary" href="/auth/discord?mode=link">LINK DISCORD</a><hr /><h2>Change password</h2><form method="POST" action="?/changePassword"><label class="field">CURRENT PASSWORD<input name="currentPassword" type="password" required /></label><label class="field">NEW PASSWORD<input name="newPassword" type="password" minlength="12" required /></label><label class="field">CONFIRM PASSWORD<input name="confirmPassword" type="password" minlength="12" required /></label>{#if form?.passwordError}<p class="error">{form.passwordError}</p>{/if}<div class="form-actions"><button class="primary">UPDATE PASSWORD</button></div></form></aside>
</div></main>
<main class="page profile-page">
<section class="profile-command">
<div class="profile-ident"><span class="profile-avatar">{user.username.slice(0,1).toUpperCase()}</span><div><p class="eyebrow"><span></span> AGENT PROFILE</p><h1>{user.username}</h1><span class="role-badge">{user.role.toUpperCase()} ACCESS</span></div></div>
<div class="profile-metrics"><div><strong>{data.account.builds.length}</strong><span>BUILDS</span></div><div><strong>{data.account.guides.length}</strong><span>GUIDES</span></div><div><strong>{data.account.favorites.length}</strong><span>FAVORITES</span></div><div><strong>{data.account.ratings.length}</strong><span>RATED</span></div></div>
</section>
{#if data.chooseUsername}<div class="account-alert"><b>IDENTITY SETUP REQUIRED</b><span>Choose the unique SHD.build username other agents will see.</span></div>{/if}
{#if data.discordLinked}<p class="success">Discord account linked successfully.</p>{/if}
{#if data.oauthError}<p class="error">Discord linking could not be completed.</p>{/if}
<div class="profile-dashboard">
<section class="profile-feed">
<div class="profile-module"><div class="module-head"><div><p class="eyebrow">LOADOUT ARCHIVE</p><h2>MY BUILDS</h2></div><a class="secondary" href="/builder">NEW BUILD +</a></div>
{#if data.account.builds.length}<div class="profile-table"><div class="profile-row heading"><span>LOADOUT</span><span>VISIBILITY</span><span>UPDATED</span><span></span></div>{#each data.account.builds as build}<div class="profile-row"><div><strong>{build.title}</strong><small>/{build.slug}</small></div><span>{build.visibility}</span><span>{new Date(String(build.updated_at)).toLocaleDateString()}</span><button>OPEN →</button></div>{/each}</div>{:else}<div class="profile-empty"><span>◇</span><b>NO BUILDS RECORDED</b><p>Your saved and published loadouts will appear here.</p><a href="/builder">CREATE YOUR FIRST BUILD →</a></div>{/if}
</div>
<div class="profile-columns"><div class="profile-module"><p class="eyebrow">SAVED INTELLIGENCE</p><h2>FAVORITES</h2><strong class="large-count">{data.account.favorites.length}</strong><span class="count-label">SAVED LOADOUTS</span></div><div class="profile-module"><p class="eyebrow">FIELD FEEDBACK</p><h2>RATINGS</h2><strong class="large-count">{data.account.ratings.length}</strong><span class="count-label">RATED LOADOUTS</span></div></div>
</section>
<aside class="account-console"><div class="console-head"><span>ACCOUNT CONTROL</span><i></i></div><p class="eyebrow">PUBLIC IDENTITY</p><h2>USERNAME</h2><PendingForm action="?/changeUsername"><label class="field">PUBLIC USERNAME<input name="username" value={user.username} required /></label>{#if form?.usernameError}<p class="error">{form.usernameError}</p>{/if}<button class="primary console-action">UPDATE IDENTITY →</button></PendingForm><hr/><p class="eyebrow">CONNECTIONS</p><h2>SIGN-IN METHODS</h2><div class="connection-card"><span class="discord-node">◉</span><div><b>DISCORD</b><small>OAuth identity provider</small></div><a href="/auth/discord?mode=link">LINK →</a></div><hr/><p class="eyebrow">SECURITY</p><h2>CHANGE PASSWORD</h2><PendingForm action="?/changePassword"><label class="field">CURRENT PASSWORD<input name="currentPassword" type="password" required /></label><label class="field">NEW PASSWORD<input name="newPassword" type="password" minlength="12" required /></label><label class="field">CONFIRM PASSWORD<input name="confirmPassword" type="password" minlength="12" required /></label>{#if form?.passwordError}<p class="error">{form.passwordError}</p>{/if}<button class="primary console-action">UPDATE PASSWORD →</button></PendingForm></aside>
</div>
</main>
+2 -2
View File
@@ -30,8 +30,8 @@
</script>
<svelte:head><title>Build a loadout — SHD.build</title></svelte:head>
<main class="app-page">
<div class="page-title"><div><p class="eyebrow"><span></span> LOADOUT WORKBENCH</p><input bind:value={buildName} aria-label="Build name" /></div><div class="actions"><span class:visible={saved}>SAVED LOCALLY</span><button class="primary" onclick={saveDraft}>SAVE DRAFT</button></div></div>
<main class="app-page builder-page">
<div class="builder-command"><div class="page-title"><div><p class="eyebrow"><span></span> LOADOUT WORKBENCH</p><input bind:value={buildName} aria-label="Build name" /></div><div class="actions"><span class:visible={saved}>SAVED LOCALLY</span><button class="primary" onclick={saveDraft}>SAVE DRAFT</button></div></div><div class="builder-status"><span><i></i> DRAFT ACTIVE</span><span>6 EQUIPMENT SLOTS</span><span>LOCAL STORAGE</span></div></div>
{#if message}<p class="success" role="status">{message}</p>{/if}
<div class="builder-layout">
<section class="slot-grid">{#each slots as slot}<button class="slot-card" onclick={() => (message = `${slot.name} editing will be added in the next builder increment.`)}><span class="slot-label">{slot.name}</span><span class="slot-icon">◇</span><strong>{slot.item}</strong><small>{slot.brand}</small><b>{slot.core}</b></button>{/each}</section>
+4 -4
View File
@@ -1,9 +1,9 @@
<script lang="ts">
const builds = [
{ title: 'Striker Critical Rush', author: 'AgentNova', mode: 'PVE', rating: 4.9 },
{ title: 'Ironclad Vanguard', author: 'KelsoMain', mode: 'PVP', rating: 4.8 },
{ title: 'Status Protocol', author: 'SHD_Warden', mode: 'PVE', rating: 4.7 }
{ title: 'Striker Critical Rush', author: 'AgentNova', mode: 'PVE', rating: 4.9, votes:128, tags:['AR','CRITICAL','SOLO'], color:'#f6a821' },
{ title: 'Ironclad Vanguard', author: 'KelsoMain', mode: 'PVP', rating: 4.8, votes:94, tags:['TANK','SHIELD','TEAM'], color:'#61b7d7' },
{ title: 'Status Protocol', author: 'SHD_Warden', mode: 'PVE', rating: 4.7, votes:76, tags:['SKILL','STATUS','SUPPORT'], color:'#d36b5c' }
];
</script>
<svelte:head><title>Discover builds — SHD.build</title></svelte:head>
<main class="page"><div class="page-head"><div><p class="eyebrow">FIELD-TESTED LOADOUTS</p><h1>Discover builds</h1><p class="muted">Browse, sort, rate, and copy community builds.</p></div><a class="primary" href="/builder">NEW BUILD +</a></div><section class="grid cards">{#each builds as build}<article class="card"><small>{build.mode} · ★ {build.rating}</small><h3>{build.title}</h3><p class="muted">BY {build.author.toUpperCase()}</p><button class="secondary">VIEW BUILD →</button></article>{/each}</section></main>
<main class="page catalogue-page"><div class="catalogue-hero"><div><p class="eyebrow"><span></span> SHD LOADOUT ARCHIVE</p><h1>DISCOVER<br/><em>BUILDS.</em></h1><p>Field-tested configurations submitted by agents across Manhattan.</p></div><div class="catalogue-count"><strong>2,847</strong><span>PUBLIC LOADOUTS</span><a class="primary" href="/builder">NEW BUILD +</a></div></div><div class="filter-bar"><label>SEARCH ARCHIVE<input placeholder="Build name, weapon, agent…" /></label><label>ACTIVITY<select><option>ALL ACTIVITIES</option><option>PVE</option><option>PVP</option></select></label><label>SORT BY<select><option>TOP RATED</option><option>MOST RECENT</option><option>MOST SAVED</option></select></label><button class="secondary">FILTERS +</button></div><section class="build-grid catalogue-grid">{#each builds as build}<article style={`--build-color:${build.color}`}><div class="card-top"><span>{build.mode}</span><span>★ {build.rating}</span></div><div class="weapon-art"><div></div><span>SHD LOADOUT</span></div><div class="card-body"><p>COMMUNITY BUILD</p><h3>{build.title}</h3><div class="tags">{#each build.tags as tag}<span>{tag}</span>{/each}</div><div class="author"><span class="avatar">{build.author.slice(0,1)}</span><span>BY <strong>{build.author}</strong></span><span>{build.votes} RATINGS</span></div><button class="build-open">VIEW LOADOUT →</button></div></article>{/each}</section></main>
+1 -1
View File
@@ -1,2 +1,2 @@
<svelte:head><title>Build guides — SHD.build</title></svelte:head>
<main class="page"><div class="page-head"><div><p class="eyebrow">FIELD NOTES</p><h1>Build guides</h1><p class="muted">Detailed community strategies and loadout walkthroughs will live here.</p></div></div><div class="panel"><h2>Guides are coming next</h2><p class="muted">The route is ready for guide listing, search, authorship, and rich build-linked articles.</p></div></main>
<main class="page guides-page"><div class="catalogue-hero"><div><p class="eyebrow"><span></span> SHD FIELD MANUAL</p><h1>BUILD<br/><em>GUIDES.</em></h1><p>Long-form tactical notes, play styles, rotations, and loadout walkthroughs from experienced agents.</p></div><div class="catalogue-count"><strong>FIELD</strong><span>INTELLIGENCE NETWORK</span><a class="primary" href="/builder">CREATE A BUILD +</a></div></div><div class="guide-layout"><section class="guide-feature"><div class="guide-code">SHD // INTEL</div><p class="eyebrow">EDITORIAL SYSTEM</p><h2>FIELD REPORTS<br/>COMING ONLINE</h2><p>The guide publishing route is ready for build-linked articles. Rich writing, tactical sections, author profiles, and community discovery arrive in a later increment.</p><div class="guide-meta"><span>STATUS <b>QUEUED</b></span><span>PRIORITY <b>NORMAL</b></span><span>NODE <b>NYC-01</b></span></div></section><aside class="guide-index"><p class="eyebrow">PLANNED CHANNELS</p>{#each ['PVE OPERATIONS','PVP DOCTRINE','WEAPON ANALYSIS','TEAM COMPOSITIONS'] as item,i}<div><small>0{i+1}</small><span>{item}</span><b>›</b></div>{/each}</aside></div></main>
+2 -2
View File
@@ -1,3 +1,3 @@
<script lang="ts">let { data, form } = $props();</script>
<script lang="ts">import PendingForm from '$lib/components/PendingForm.svelte'; let { data, form } = $props();</script>
<svelte:head><title>Sign in — SHD.build</title></svelte:head>
<main class="auth-page"><section><p class="eyebrow"><span></span> AGENT ACCESS</p><h1>WELCOME BACK</h1><p>Sign in to publish builds, join discussions, and keep your loadouts synced.</p><a class="discord" href="/auth/discord">◉ CONTINUE WITH DISCORD</a><div class="divider"><span>OR USE YOUR ACCOUNT</span></div><form method="POST" action="?/login"><label>USERNAME<input name="username" value={form?.username ?? ''} autocomplete="username" required /></label><label>PASSWORD<input name="password" type="password" autocomplete="current-password" required /></label>{#if form?.loginError}<p class="error">{form.loginError}</p>{/if}{#if data.oauthError}<p class="error">{data.oauthError}</p>{/if}<button class="primary full">SIGN IN →</button></form></section></main>
<main class="auth-page"><div class="auth-frame"><section class="auth-intel"><p class="eyebrow"><span></span> SHD IDENTITY NETWORK</p><h1>AGENT<br/><em>ACCESS.</em></h1><p>Authenticate to publish builds, join field discussions, and keep your loadouts synchronized across devices.</p><div class="auth-status"><span><i></i> NETWORK ONLINE</span><span>NODE NYC-01</span></div></section><section class="auth-console"><div class="console-head"><span>SECURE SIGN-IN</span><b>SHD // AUTH</b></div><a class="discord" href="/auth/discord">◉ CONTINUE WITH DISCORD</a><div class="divider"><span>OR USE LOCAL CREDENTIALS</span></div><PendingForm action="?/login"><label class="field">USERNAME<input name="username" value={form?.username ?? ''} autocomplete="username" placeholder="Agent handle" required /></label><label class="field">PASSWORD<input name="password" type="password" autocomplete="current-password" placeholder="••••••••••••" required /></label>{#if form?.loginError}<p class="error">{form.loginError}</p>{/if}{#if data.oauthError}<p class="error">{data.oauthError}</p>{/if}<button class="primary full">AUTHENTICATE →</button></PendingForm><small>Credentials are verified securely on the server. Discord shares only the basic identity information required to link your account.</small></section></div></main>