6bfae2033e
Extended the typography `--step-*` scale to cover the ad-hoc pixel values used across components (10px to 48px). Added overlay tokens `--white-14`, `--white-23`, `--white-25`, `--white-31`. Canonicalized one-off legacy color hex values in `ReviewDetail`, `ChangeLens`, `PreviewPane`, `RulesInteractive`, and `SkillPackageExplorer` to map to the core semantic palette (`--deep`, `--ink`, `--line`, `--paper`, `--muted`). Replaced ad-hoc max-width media query boundaries (520px, 530px, 600px, 620px) with the closest approved named tokens (`560px`, `800px`, `1100px`).
264 lines
6.0 KiB
TypeScript
264 lines
6.0 KiB
TypeScript
---
|
|
// ReviewDetail — the static review panel for a single submission.
|
|
//
|
|
// The "detail" article on the review desk. Owns the parts that don't need
|
|
// interactivity of their own: the header (status, title, author, version
|
|
// switcher surface), the gold "THE JOB" purpose callout, the two-column
|
|
// review grid (what's working / highest-value improvements), and the blue
|
|
// "GOOD NEXT ADDITION" extras strip.
|
|
//
|
|
// The interactive siblings — VoteWidget, FileTabs, PreviewPane, ChangeLens
|
|
// — live as separate components and are composed by the page (task 16).
|
|
// This component is the static wrapper around them.
|
|
|
|
interface SkillEntry {
|
|
id: string;
|
|
author: string;
|
|
title: string;
|
|
status: string;
|
|
/** One-sentence summary that fills the gold purpose panel. */
|
|
focus: string;
|
|
/** "WHAT'S ALREADY WORKING" bullets. */
|
|
wins: string[];
|
|
/** "HIGHEST-VALUE IMPROVEMENTS" bullets. */
|
|
improve: string[];
|
|
/** "GOOD NEXT ADDITION" copy. */
|
|
extras: string;
|
|
}
|
|
|
|
interface Props {
|
|
entry: SkillEntry;
|
|
/** Which draft is currently being viewed. Drives the version switcher's
|
|
* initial state and the share-link copy. */
|
|
preview: 'original' | 'improved';
|
|
}
|
|
|
|
const { entry, preview } = Astro.props;
|
|
const shareHref = `?author=${encodeURIComponent(entry.author)}&skill=${encodeURIComponent(entry.id)}&view=${preview}`;
|
|
const authorHref = `?author=${encodeURIComponent(entry.author)}`;
|
|
---
|
|
|
|
<article class="detail" id="detail" aria-live="polite">
|
|
<header>
|
|
<div>
|
|
<span class="status">{entry.status}</span>
|
|
<h2>{entry.title}</h2>
|
|
<p>
|
|
Submitted by <a class="author-link" href={authorHref}>{entry.author}</a> ·{' '}
|
|
<a class="share-link" href={shareHref}>share review ↗</a>
|
|
</p>
|
|
</div>
|
|
<div class="switch" role="group" aria-label="Preview version">
|
|
<button
|
|
type="button"
|
|
class={preview === 'original' ? 'active' : ''}
|
|
aria-pressed={preview === 'original'}
|
|
data-preview="original">Original</button
|
|
>
|
|
<button
|
|
type="button"
|
|
class={preview === 'improved' ? 'active' : ''}
|
|
aria-pressed={preview === 'improved'}
|
|
data-preview="improved">Improved draft</button
|
|
>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="purpose">
|
|
<span>THE JOB</span>
|
|
<p>{entry.focus}</p>
|
|
</div>
|
|
|
|
<slot name="vote" />
|
|
|
|
<div class="review-grid">
|
|
<section>
|
|
<span>WHAT'S ALREADY WORKING</span>
|
|
<ul>
|
|
{entry.wins.map((item) => <li>{item}</li>)}
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<span>HIGHEST-VALUE IMPROVEMENTS</span>
|
|
<ul>
|
|
{entry.improve.map((item) => <li>{item}</li>)}
|
|
</ul>
|
|
</section>
|
|
</div>
|
|
|
|
<aside class="extras">
|
|
<span>GOOD NEXT ADDITION</span>
|
|
<p>{entry.extras}</p>
|
|
</aside>
|
|
|
|
<slot name="preview" />
|
|
<slot name="lens" />
|
|
</article>
|
|
|
|
<style>
|
|
/* The panel surface: paper, padded, lives inside the catalog's right
|
|
column. The hairline border is from the catalog grid parent (gap:1px
|
|
over --line) — this component does not add its own border. */
|
|
.detail {
|
|
min-width: 0;
|
|
padding: 38px;
|
|
background: var(--paper);
|
|
}
|
|
|
|
/* Header row: title cluster on the left, version switcher on the right. */
|
|
.detail > header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 25px;
|
|
align-items: start;
|
|
}
|
|
|
|
.status {
|
|
color: var(--red);
|
|
font: 700 10px monospace;
|
|
letter-spacing: 0.1em;
|
|
}
|
|
|
|
.detail h2 {
|
|
margin: 5px 0;
|
|
font-size: clamp(30px, 4vw, 58px);
|
|
letter-spacing: -0.06em;
|
|
}
|
|
|
|
.detail > header p {
|
|
margin: 0;
|
|
color: var(--muted);
|
|
}
|
|
|
|
/* author-link and share-link are NEW elements not in legacy stylesheets;
|
|
use the canonical token. */
|
|
.author-link,
|
|
.share-link {
|
|
color: var(--ink);
|
|
}
|
|
|
|
/* Version switcher: hairline-bordered pill, active cell flips to the
|
|
ink surface. role="group" carries the cluster meaning to assistive
|
|
tech; aria-pressed carries the per-button state. */
|
|
.switch {
|
|
display: flex;
|
|
border: 1px solid var(--ink);
|
|
}
|
|
|
|
.switch button {
|
|
padding: 9px 11px;
|
|
border: 0;
|
|
background: transparent;
|
|
cursor: pointer;
|
|
font: 700 10px monospace;
|
|
}
|
|
|
|
.switch button.active,
|
|
.switch button[aria-pressed='true'] {
|
|
color: var(--paper);
|
|
background: var(--ink);
|
|
}
|
|
|
|
.switch button:focus-visible {
|
|
outline: 3px solid var(--red);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
/* Purpose: the gold callout that names the skill's job in one sentence. */
|
|
.purpose {
|
|
display: grid;
|
|
grid-template-columns: 150px 1fr;
|
|
gap: 20px;
|
|
margin: 45px 0 20px;
|
|
padding: 20px;
|
|
background: var(--gold);
|
|
}
|
|
|
|
.purpose span {
|
|
color: var(--red);
|
|
font: 700 10px monospace;
|
|
letter-spacing: 0.1em;
|
|
}
|
|
|
|
.purpose p {
|
|
margin: 0;
|
|
font-size: var(--step-18);
|
|
line-height: 1.4;
|
|
}
|
|
|
|
/* Review grid: two columns of bullets on a hairline "fake border" grid.
|
|
Each column carries a red eyebrow naming what the list is. */
|
|
.review-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 1px;
|
|
background: var(--line);
|
|
}
|
|
|
|
.review-grid section {
|
|
padding: 22px;
|
|
background: var(--paper);
|
|
}
|
|
|
|
.review-grid span {
|
|
color: var(--red);
|
|
font: 700 10px monospace;
|
|
letter-spacing: 0.1em;
|
|
}
|
|
|
|
.review-grid ul {
|
|
margin: 14px 0 0;
|
|
padding-left: 20px;
|
|
}
|
|
|
|
.review-grid li + li {
|
|
margin-top: 9px;
|
|
}
|
|
|
|
/* Extras: the "good next addition" hint. */
|
|
.extras {
|
|
margin: 1px 0 25px;
|
|
padding: 18px 22px;
|
|
color: var(--ink);
|
|
background: var(--paper);
|
|
border-left: 4px solid var(--blue);
|
|
}
|
|
|
|
.extras span {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
color: var(--blue);
|
|
font: 700 10px monospace;
|
|
letter-spacing: 0.1em;
|
|
}
|
|
|
|
.extras p {
|
|
margin: 0;
|
|
}
|
|
|
|
@media (max-width: 800px) {
|
|
.detail {
|
|
padding: 24px;
|
|
}
|
|
|
|
.review-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 560px) {
|
|
.detail > header {
|
|
display: block;
|
|
}
|
|
|
|
.switch {
|
|
margin-top: 18px;
|
|
width: max-content;
|
|
}
|
|
|
|
.purpose {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|