feat(llm-security): playground v7.6.0 fase 1-2 — fjern DS-duplikater + page-shell harmonisering
Slett ~50 duplikat-CSS-deklarasjoner fra playground-ens <style>-blokk
som overstyrte DS Tier 3 supplement uten gevinst (.app-shell, .tab-list,
.fleet-tile*, .form-progress, .eyebrow, .page__*, .key-stat*, .field-*,
.expansion (ekskl. body), .stack-*, .card*, .tracks*, .checkbox-row).
JS-fix: 4 modifier-strenger oppdatert fra forkortede ('crit', 'med')
til DS-konsistente fulle navn ('critical', 'medium') i renderKeyStatsGrid-data.
Konsekvens: DS vinner cascade-en, eliminerer subtile visuelle drift
mellom playground og referanse-scenarioer.
Page-shell harmonisering: alle 4 overflater (onboarding, home, catalog,
project) bruker nå DS page__header-klyngen via renderPageShell. Onboarding
konvertert fra custom <header class="onboarding-header"> til samme mønster.
renderPageShell utvidet med opts.meta (page__meta) og opts.hero
(page__header--hero modifier). Hero-mønster på home med
clamp(36px, 5vw, 56px) og letter-spacing -0.025em.
Behold til Sesjon 2: .verdict-pill (erstattes av verdict-pill-lg fase 3),
.form-progress__step* (erstattes av fp-step fase 4), .multi-select
(bevisst input-box-look), .expansion__body (markup-mismatch m/ DS-anim).
Forberedelse til v7.6.0 — Tier 3 referanse-case.
This commit is contained in:
parent
ce3891bdd0
commit
9ef0c48c00
1 changed files with 63 additions and 111 deletions
|
|
@ -43,24 +43,24 @@
|
||||||
<link rel="stylesheet" href="vendor/playground-design-system/print.css" media="print">
|
<link rel="stylesheet" href="vendor/playground-design-system/print.css" media="print">
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* App-shell layout. Vendored design-system levner komponent-CSS;
|
/* Playground-spesifikk layout. Alt komponent-CSS som har en DS-pendant er
|
||||||
her bor kun side-spesifikk layout-grid (sidebar+main, modals, sub-cards). */
|
fjernet i v7.6.0 fase 1 — DS Tier 3-supplement vinner cascade. Her bor
|
||||||
|
kun side-spesifikk layout-grid (sidebar+main, modals), playground-only
|
||||||
|
komponenter (.tracks, .verdict-pill, .form-progress__step*, .field-from-tag),
|
||||||
|
og bevisste overskrivinger som DS ikke dekker (.expansion__body markup,
|
||||||
|
.multi-select fieldset-ramme, .checkbox-row accent-color). */
|
||||||
main#app { min-height: 100vh; padding: 0; }
|
main#app { min-height: 100vh; padding: 0; }
|
||||||
[hidden] { display: none !important; }
|
[hidden] { display: none !important; }
|
||||||
|
|
||||||
/* Onboarding-layout: sidebar + main */
|
/* Onboarding-layout: sidebar + main */
|
||||||
.onboarding-layout { display: grid; grid-template-columns: 280px 1fr; gap: var(--space-6); align-items: start; }
|
.onboarding-layout { display: grid; grid-template-columns: 280px 1fr; gap: var(--space-6); align-items: start; }
|
||||||
@media (max-width: 880px) { .onboarding-layout { grid-template-columns: 1fr; } .form-progress { position: static; width: auto; } }
|
@media (max-width: 880px) { .onboarding-layout { grid-template-columns: 1fr; } .form-progress { position: static; width: auto; } }
|
||||||
.onboarding-header { margin-bottom: var(--space-5); }
|
|
||||||
.onboarding-header h1 { font-size: var(--font-size-2xl); margin: 0 0 var(--space-2); }
|
|
||||||
.onboarding-header p { color: var(--color-text-secondary); margin: 0; max-width: 60ch; }
|
|
||||||
.onboarding-groups { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-6); }
|
.onboarding-groups { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-6); }
|
||||||
.onboarding-fields { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-2) 0; }
|
.onboarding-fields { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-2) 0; }
|
||||||
.onboarding-actions { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) 0; flex-wrap: wrap; }
|
.onboarding-actions { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) 0; flex-wrap: wrap; }
|
||||||
.onboarding-help { font-size: var(--font-size-sm); color: var(--color-text-tertiary); }
|
.onboarding-help { font-size: var(--font-size-sm); color: var(--color-text-tertiary); }
|
||||||
|
|
||||||
/* Home + project list */
|
/* Home + project list */
|
||||||
.home-hero { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-5); }
|
|
||||||
.home-section-head { display: flex; align-items: baseline; justify-content: space-between; margin: var(--space-6) 0 var(--space-3); }
|
.home-section-head { display: flex; align-items: baseline; justify-content: space-between; margin: var(--space-6) 0 var(--space-3); }
|
||||||
.home-section-head h2 { font-size: var(--font-size-xl); }
|
.home-section-head h2 { font-size: var(--font-size-xl); }
|
||||||
.home-section-head .home-section-meta { color: var(--color-text-tertiary); font-size: var(--font-size-sm); }
|
.home-section-head .home-section-meta { color: var(--color-text-tertiary); font-size: var(--font-size-sm); }
|
||||||
|
|
@ -85,33 +85,7 @@
|
||||||
.project-header__chip { display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px; border-radius: var(--radius-sm); background: var(--color-bg-soft); color: var(--color-text-secondary); font-size: var(--font-size-xs); font-family: var(--font-family-mono); }
|
.project-header__chip { display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px; border-radius: var(--radius-sm); background: var(--color-bg-soft); color: var(--color-text-secondary); font-size: var(--font-size-xs); font-family: var(--font-family-mono); }
|
||||||
.scenario-tag { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: var(--radius-pill); background: var(--color-scope-security-soft, var(--color-primary-100)); color: var(--color-scope-security-on, var(--color-primary-700)); font-size: var(--font-size-xs); font-weight: var(--font-weight-medium); }
|
.scenario-tag { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: var(--radius-pill); background: var(--color-scope-security-soft, var(--color-primary-100)); color: var(--color-scope-security-on, var(--color-primary-700)); font-size: var(--font-size-xs); font-weight: var(--font-weight-medium); }
|
||||||
|
|
||||||
/* Tracks (DS) som hero på home — fallback hvis ikke i tier3 wave 2 */
|
/* Command form patterns (playground-only — DS dekker ikke command-form) */
|
||||||
.tracks { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); margin-bottom: var(--space-5); }
|
|
||||||
@media (max-width: 880px) { .tracks { grid-template-columns: 1fr; } }
|
|
||||||
.tracks__card { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-5); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); background: var(--color-surface); cursor: pointer; text-align: left; font-family: inherit; color: var(--color-text-primary); transition: border-color 120ms, transform 120ms; }
|
|
||||||
.tracks__card:hover { border-color: var(--color-scope-security, var(--color-primary-500)); transform: translateY(-1px); }
|
|
||||||
.tracks__card-icon { font-size: var(--font-size-2xl); width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; }
|
|
||||||
.tracks__card-title { font-size: var(--font-size-lg); margin: 0; font-weight: var(--font-weight-semibold); }
|
|
||||||
.tracks__card-desc { font-size: var(--font-size-sm); color: var(--color-text-secondary); margin: 0; }
|
|
||||||
.tracks__card-meta { display: flex; justify-content: space-between; font-size: var(--font-size-xs); color: var(--color-text-tertiary); margin-top: var(--space-2); }
|
|
||||||
.tracks__card-cta { color: var(--color-scope-security, var(--color-primary-500)); font-weight: var(--font-weight-semibold); }
|
|
||||||
|
|
||||||
/* Fleet-grid (project list) */
|
|
||||||
.fleet-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--space-3); }
|
|
||||||
.fleet-tile { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-4); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); background: var(--color-surface); cursor: pointer; text-align: left; font-family: inherit; color: var(--color-text-primary); transition: border-color 120ms; }
|
|
||||||
.fleet-tile:hover { border-color: var(--color-scope-security, var(--color-primary-500)); }
|
|
||||||
.fleet-tile__row { display: flex; justify-content: space-between; align-items: center; gap: var(--space-2); }
|
|
||||||
.fleet-tile__name { font-weight: var(--font-weight-semibold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
||||||
.fleet-tile__chip { font-size: var(--font-size-xs); padding: 2px 8px; border-radius: var(--radius-pill); background: var(--color-bg-soft); color: var(--color-text-secondary); white-space: nowrap; }
|
|
||||||
.fleet-tile__meter { width: 100%; height: 6px; background: var(--color-bg-soft); border-radius: var(--radius-pill); overflow: hidden; }
|
|
||||||
.fleet-tile__meter-fill { display: block; height: 100%; border-radius: inherit; transition: width 200ms; }
|
|
||||||
.fleet-tile__meter-fill[data-band="1"] { background: var(--color-severity-low); }
|
|
||||||
.fleet-tile__meter-fill[data-band="2"] { background: var(--color-severity-medium); }
|
|
||||||
.fleet-tile__meter-fill[data-band="3"] { background: var(--color-severity-high); }
|
|
||||||
.fleet-tile__meter-fill[data-band="4"] { background: var(--color-severity-critical); }
|
|
||||||
.fleet-tile__meta { display: flex; justify-content: space-between; font-size: var(--font-size-xs); color: var(--color-text-tertiary); }
|
|
||||||
|
|
||||||
/* Command form patterns */
|
|
||||||
.command-form { display: flex; flex-direction: column; gap: var(--space-3); }
|
.command-form { display: flex; flex-direction: column; gap: var(--space-3); }
|
||||||
.command-form__fields { display: flex; flex-direction: column; gap: var(--space-3); }
|
.command-form__fields { display: flex; flex-direction: column; gap: var(--space-3); }
|
||||||
.command-form__actions { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
|
.command-form__actions { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
|
||||||
|
|
@ -121,46 +95,30 @@
|
||||||
.form-preview__heading { font-size: var(--font-size-xs); font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); margin: 0 0 var(--space-2); }
|
.form-preview__heading { font-size: var(--font-size-xs); font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); margin: 0 0 var(--space-2); }
|
||||||
.code-block { font-family: var(--font-family-mono); font-size: var(--font-size-sm); color: var(--color-text-primary); margin: 0; white-space: pre-wrap; word-break: break-all; }
|
.code-block { font-family: var(--font-family-mono); font-size: var(--font-size-sm); color: var(--color-text-primary); margin: 0; white-space: pre-wrap; word-break: break-all; }
|
||||||
|
|
||||||
/* Field row */
|
/* Field utility (DS dekker .field-row/.field-label/.field-help/.required-mark/.checkbox-row) */
|
||||||
.field-row { display: flex; flex-direction: column; gap: 6px; }
|
.field-from-tag { font-size: 10px; font-weight: var(--font-weight-medium); padding: 1px 6px; border-radius: var(--radius-pill); background: var(--color-scope-security-soft, var(--color-primary-100)); color: var(--color-scope-security-on, var(--color-primary-700)); text-transform: uppercase; letter-spacing: 0.06em; cursor: help; margin-left: 6px; vertical-align: middle; }
|
||||||
.field-label { font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); color: var(--color-text-primary); display: flex; align-items: center; gap: 6px; }
|
|
||||||
.field-from-tag { font-size: 10px; font-weight: var(--font-weight-medium); padding: 1px 6px; border-radius: var(--radius-pill); background: var(--color-scope-security-soft, var(--color-primary-100)); color: var(--color-scope-security-on, var(--color-primary-700)); text-transform: uppercase; letter-spacing: 0.06em; cursor: help; }
|
|
||||||
.field-help { font-size: var(--font-size-xs); color: var(--color-text-tertiary); }
|
|
||||||
.input, .textarea, .select { font-family: inherit; font-size: var(--font-size-sm); padding: 8px 10px; border: 1px solid var(--color-border-moderate); border-radius: var(--radius-sm); background: var(--color-surface); color: var(--color-text-primary); transition: border-color 120ms ease, box-shadow 120ms ease; }
|
.input, .textarea, .select { font-family: inherit; font-size: var(--font-size-sm); padding: 8px 10px; border: 1px solid var(--color-border-moderate); border-radius: var(--radius-sm); background: var(--color-surface); color: var(--color-text-primary); transition: border-color 120ms ease, box-shadow 120ms ease; }
|
||||||
.input:hover, .textarea:hover, .select:hover { border-color: var(--color-border-strong); }
|
.input:hover, .textarea:hover, .select:hover { border-color: var(--color-border-strong); }
|
||||||
.input:focus, .textarea:focus, .select:focus { outline: 2px solid var(--color-scope-security, var(--color-primary-500)); outline-offset: 1px; border-color: var(--color-border-strong); }
|
.input:focus, .textarea:focus, .select:focus { outline: 2px solid var(--color-scope-security, var(--color-primary-500)); outline-offset: 1px; border-color: var(--color-border-strong); }
|
||||||
.textarea { resize: vertical; font-family: inherit; }
|
.textarea { resize: vertical; font-family: inherit; }
|
||||||
|
/* Multi-select: bevisst input-box-look (border + padding) — overstyrer DS' flate liste */
|
||||||
.multi-select { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; border: 1px solid var(--color-border-moderate); border-radius: var(--radius-sm); background: var(--color-surface); }
|
.multi-select { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; border: 1px solid var(--color-border-moderate); border-radius: var(--radius-sm); background: var(--color-surface); }
|
||||||
.multi-select:hover { border-color: var(--color-border-strong); }
|
.multi-select:hover { border-color: var(--color-border-strong); }
|
||||||
.checkbox-row { display: flex; align-items: center; gap: 8px; font-size: var(--font-size-sm); cursor: pointer; }
|
/* Checkbox accent-color: playground-spesifikk styling. .checkbox-row selve dekkes av DS. */
|
||||||
.checkbox-row input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--color-scope-security, var(--color-primary-500)); border: 1px solid var(--color-border-strong); }
|
.checkbox-row input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--color-scope-security, var(--color-primary-500)); border: 1px solid var(--color-border-strong); }
|
||||||
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
||||||
|
|
||||||
/* Card patterns */
|
|
||||||
.card { border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); background: var(--color-surface); padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-3); }
|
|
||||||
.card__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); }
|
|
||||||
.card__title { font-size: var(--font-size-md); font-weight: var(--font-weight-semibold); margin: 0 0 4px; }
|
|
||||||
.card__desc { font-size: var(--font-size-sm); color: var(--color-text-secondary); margin: 0; }
|
|
||||||
.card__hint { display: inline-block; padding: 2px 6px; border-radius: var(--radius-sm); background: var(--color-bg-soft); font-family: var(--font-family-mono); font-size: 11px; color: var(--color-text-tertiary); margin-top: 6px; }
|
|
||||||
.card__pill { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: var(--radius-pill); background: var(--color-bg-soft); color: var(--color-text-secondary); font-size: var(--font-size-xs); font-weight: var(--font-weight-medium); white-space: nowrap; }
|
|
||||||
.card__actions { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
|
|
||||||
|
|
||||||
/* Catalog */
|
/* Catalog */
|
||||||
.catalog-search { width: 100%; max-width: 480px; margin-bottom: var(--space-5); }
|
.catalog-search { width: 100%; max-width: 480px; margin-bottom: var(--space-5); }
|
||||||
.catalog-cards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: var(--space-3); margin-top: var(--space-3); }
|
.catalog-cards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: var(--space-3); margin-top: var(--space-3); }
|
||||||
.catalog-tool-notice { padding: 8px 12px; background: var(--color-bg-soft); border-left: 3px solid var(--color-state-info, var(--color-primary-300)); font-size: var(--font-size-xs); color: var(--color-text-secondary); border-radius: var(--radius-sm); }
|
.catalog-tool-notice { padding: 8px 12px; background: var(--color-bg-soft); border-left: 3px solid var(--color-state-info, var(--color-primary-300)); font-size: var(--font-size-xs); color: var(--color-text-secondary); border-radius: var(--radius-sm); }
|
||||||
.expansion { border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); overflow: hidden; background: var(--color-surface); margin-bottom: var(--space-3); }
|
/* Expansion-body: playground-markup mangler .expansion__body-inner-wrapping
|
||||||
.expansion__head { display: flex; align-items: center; gap: var(--space-3); width: 100%; padding: var(--space-3) var(--space-4); background: transparent; border: 0; cursor: pointer; font-family: inherit; text-align: left; color: var(--color-text-primary); }
|
som DS' grid-template-rows-animasjon krever. Beholdes til markup-en
|
||||||
.expansion__head:hover { background: var(--color-bg-soft); }
|
evt. oppgraderes (out-of-scope for v7.6.0). */
|
||||||
.expansion__title { flex: 1; display: flex; flex-direction: column; gap: 2px; }
|
|
||||||
.expansion__title-main { font-weight: var(--font-weight-semibold); font-size: var(--font-size-md); }
|
|
||||||
.expansion__title-sub { font-size: var(--font-size-xs); color: var(--color-text-tertiary); }
|
|
||||||
.expansion__chev { font-size: var(--font-size-md); color: var(--color-text-tertiary); transition: transform 120ms; }
|
|
||||||
.expansion[aria-expanded="true"] .expansion__chev { transform: rotate(180deg); }
|
|
||||||
.expansion__body { padding: 0 var(--space-4) var(--space-4); border-top: 1px solid var(--color-border-subtle); }
|
.expansion__body { padding: 0 var(--space-4) var(--space-4); border-top: 1px solid var(--color-border-subtle); }
|
||||||
.expansion[aria-expanded="false"] .expansion__body { display: none; }
|
.expansion[aria-expanded="false"] .expansion__body { display: none; }
|
||||||
|
|
||||||
/* Modal */
|
/* Modal (playground-only — DS har ikke modal-pattern enda) */
|
||||||
.modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 100; padding: var(--space-5); }
|
.modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 100; padding: var(--space-5); }
|
||||||
.modal { background: var(--color-surface); border-radius: var(--radius-md); max-width: 720px; width: 100%; max-height: 90vh; overflow: auto; padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-4); }
|
.modal { background: var(--color-surface); border-radius: var(--radius-md); max-width: 720px; width: 100%; max-height: 90vh; overflow: auto; padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-4); }
|
||||||
.modal__head { display: flex; justify-content: space-between; align-items: center; gap: var(--space-3); }
|
.modal__head { display: flex; justify-content: space-between; align-items: center; gap: var(--space-3); }
|
||||||
|
|
@ -168,47 +126,19 @@
|
||||||
.modal__close { background: transparent; border: 0; cursor: pointer; font-size: 24px; line-height: 1; padding: 4px 8px; color: var(--color-text-tertiary); }
|
.modal__close { background: transparent; border: 0; cursor: pointer; font-size: 24px; line-height: 1; padding: 4px 8px; color: var(--color-text-tertiary); }
|
||||||
.modal__close:hover { color: var(--color-text-primary); }
|
.modal__close:hover { color: var(--color-text-primary); }
|
||||||
|
|
||||||
/* Page shell */
|
/* Page-shell hero-modifier — clamp font-size for home-overflate.
|
||||||
.page__header { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); padding: var(--space-5) 0 var(--space-4); border-bottom: 1px solid var(--color-border-subtle); margin-bottom: var(--space-5); }
|
DS' .page__title er 3xl (~32px); hero-modifier vipper opp til 36-56px
|
||||||
.page__header-main { display: flex; flex-direction: column; gap: var(--space-2); flex: 1; }
|
med editorial letter-spacing. */
|
||||||
.page__eyebrow { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-scope-security, var(--color-primary-500)); font-weight: var(--font-weight-semibold); }
|
.page__header--hero .page__title { font-size: clamp(36px, 5vw, 56px); letter-spacing: -0.025em; }
|
||||||
.page__title { font-size: var(--font-size-2xl); margin: 0; }
|
|
||||||
.page__lede { font-size: var(--font-size-md); color: var(--color-text-secondary); margin: 0; max-width: 65ch; }
|
|
||||||
.page__header-aside { flex-shrink: 0; }
|
|
||||||
.key-stats { display: flex; gap: var(--space-5); flex-wrap: wrap; padding: var(--space-3) 0; background: transparent; border: 0; border-radius: 0; border-bottom: 1px solid var(--color-border-subtle); margin: 0 0 var(--space-5); }
|
|
||||||
.key-stat { display: flex; flex-direction: column; gap: 2px; min-width: 120px; }
|
|
||||||
.key-stat__label { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); }
|
|
||||||
.key-stat__value { font-size: var(--font-size-xl); font-weight: var(--font-weight-bold); font-variant-numeric: tabular-nums; }
|
|
||||||
.key-stat__hint { font-size: var(--font-size-xs); color: var(--color-text-secondary); }
|
|
||||||
.key-stat--crit .key-stat__value { color: var(--color-severity-critical); }
|
|
||||||
.key-stat--high .key-stat__value { color: var(--color-severity-high); }
|
|
||||||
.key-stat--med .key-stat__value { color: var(--color-severity-medium); }
|
|
||||||
.key-stat--low .key-stat__value { color: var(--color-severity-low); }
|
|
||||||
|
|
||||||
/* Verdict pill */
|
/* Verdict-pill (playground-only — Sesjon 2 erstatter med .verdict-pill-lg fra DS) */
|
||||||
.verdict-pill { display: inline-flex; align-items: center; padding: 4px 12px; border-radius: var(--radius-pill); font-size: var(--font-size-xs); font-weight: var(--font-weight-bold); letter-spacing: 0.06em; text-transform: uppercase; }
|
.verdict-pill { display: inline-flex; align-items: center; padding: 4px 12px; border-radius: var(--radius-pill); font-size: var(--font-size-xs); font-weight: var(--font-weight-bold); letter-spacing: 0.06em; text-transform: uppercase; }
|
||||||
.verdict-pill[data-verdict="go"], .verdict-pill[data-verdict="approved"], .verdict-pill[data-verdict="allow"] { background: var(--color-state-success-soft, var(--color-severity-low-soft)); color: var(--color-state-success, var(--color-severity-low-on)); }
|
.verdict-pill[data-verdict="go"], .verdict-pill[data-verdict="approved"], .verdict-pill[data-verdict="allow"] { background: var(--color-state-success-soft, var(--color-severity-low-soft)); color: var(--color-state-success, var(--color-severity-low-on)); }
|
||||||
.verdict-pill[data-verdict="warning"], .verdict-pill[data-verdict="go-with-conditions"] { background: var(--color-severity-medium-soft); color: var(--color-severity-medium-on); }
|
.verdict-pill[data-verdict="warning"], .verdict-pill[data-verdict="go-with-conditions"] { background: var(--color-severity-medium-soft); color: var(--color-severity-medium-on); }
|
||||||
.verdict-pill[data-verdict="block"], .verdict-pill[data-verdict="failed"] { background: var(--color-severity-critical-soft); color: var(--color-severity-critical-on); }
|
.verdict-pill[data-verdict="block"], .verdict-pill[data-verdict="failed"] { background: var(--color-severity-critical-soft); color: var(--color-severity-critical-on); }
|
||||||
.verdict-pill[data-verdict="n-a"] { background: var(--color-bg-soft); color: var(--color-text-tertiary); }
|
.verdict-pill[data-verdict="n-a"] { background: var(--color-bg-soft); color: var(--color-text-tertiary); }
|
||||||
|
|
||||||
/* Stack utilities */
|
/* Form-progress steps (playground-only — Sesjon 2 erstatter med .fp-step fra DS) */
|
||||||
.stack-sm > * + * { margin-top: var(--space-2); }
|
|
||||||
.stack-md > * + * { margin-top: var(--space-3); }
|
|
||||||
.stack-lg > * + * { margin-top: var(--space-5); }
|
|
||||||
|
|
||||||
/* App-shell */
|
|
||||||
.app-shell { max-width: 1200px; margin: 0 auto; padding: 0 var(--space-5) var(--space-12); }
|
|
||||||
.app-shell--wide { max-width: 1440px; }
|
|
||||||
|
|
||||||
/* Tab list (project screen tabs) */
|
|
||||||
.tab-list { display: flex; gap: 2px; margin-bottom: var(--space-4); border-bottom: 1px solid var(--color-border-subtle); }
|
|
||||||
.tab { background: transparent; border: 0; padding: 8px 14px; cursor: pointer; font-family: inherit; font-size: var(--font-size-sm); color: var(--color-text-secondary); border-bottom: 2px solid transparent; margin-bottom: -1px; }
|
|
||||||
.tab:hover { color: var(--color-text-primary); }
|
|
||||||
.tab[aria-current="true"] { color: var(--color-text-primary); border-bottom-color: var(--color-scope-security, var(--color-primary-500)); font-weight: var(--font-weight-semibold); }
|
|
||||||
|
|
||||||
/* Form-progress sidebar (onboarding) */
|
|
||||||
.form-progress { position: sticky; top: var(--space-5); padding: var(--space-4); background: var(--color-surface); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); display: flex; flex-direction: column; gap: var(--space-2); }
|
|
||||||
.form-progress__heading { font-size: var(--font-size-xs); font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); margin: 0 0 var(--space-2); }
|
.form-progress__heading { font-size: var(--font-size-xs); font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); margin: 0 0 var(--space-2); }
|
||||||
.form-progress__step { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--radius-sm); font-size: var(--font-size-sm); cursor: pointer; background: transparent; border: 0; font-family: inherit; text-align: left; color: var(--color-text-secondary); }
|
.form-progress__step { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--radius-sm); font-size: var(--font-size-sm); cursor: pointer; background: transparent; border: 0; font-family: inherit; text-align: left; color: var(--color-text-secondary); }
|
||||||
.form-progress__step:hover { background: var(--color-bg-soft); }
|
.form-progress__step:hover { background: var(--color-bg-soft); }
|
||||||
|
|
@ -216,12 +146,6 @@
|
||||||
.form-progress__step-marker { width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid var(--color-border-default); display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: var(--font-weight-bold); color: var(--color-text-tertiary); flex-shrink: 0; }
|
.form-progress__step-marker { width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid var(--color-border-default); display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: var(--font-weight-bold); color: var(--color-text-tertiary); flex-shrink: 0; }
|
||||||
.form-progress__step--done .form-progress__step-marker { background: var(--color-scope-security, var(--color-primary-500)); border-color: var(--color-scope-security, var(--color-primary-500)); color: var(--color-text-on-primary, white); }
|
.form-progress__step--done .form-progress__step-marker { background: var(--color-scope-security, var(--color-primary-500)); border-color: var(--color-scope-security, var(--color-primary-500)); color: var(--color-text-on-primary, white); }
|
||||||
.form-progress__step[aria-current="step"] .form-progress__step-marker { border-color: var(--color-scope-security, var(--color-primary-500)); color: var(--color-scope-security, var(--color-primary-500)); }
|
.form-progress__step[aria-current="step"] .form-progress__step-marker { border-color: var(--color-scope-security, var(--color-primary-500)); color: var(--color-scope-security, var(--color-primary-500)); }
|
||||||
|
|
||||||
/* Eyebrow utility */
|
|
||||||
.eyebrow { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-scope-security, var(--color-primary-500)); font-weight: var(--font-weight-semibold); display: block; margin-bottom: var(--space-2); }
|
|
||||||
|
|
||||||
/* Required mark */
|
|
||||||
.required-mark { color: var(--color-severity-critical); margin-left: 2px; }
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -6707,13 +6631,12 @@
|
||||||
const orgName = (store.state.shared.organization && store.state.shared.organization.name) || '';
|
const orgName = (store.state.shared.organization && store.state.shared.organization.name) || '';
|
||||||
const isReturning = !!orgName;
|
const isReturning = !!orgName;
|
||||||
|
|
||||||
const headerHtml = (
|
const headerHtml = renderPageShell({
|
||||||
'<header class="onboarding-header">' +
|
eyebrow: (isReturning ? 'RE-ONBOARDING' : 'ONBOARDING') + ' · ' + allCompleteCount + ' av ' + ONBOARDING_GROUPS.length + ' grupper komplette',
|
||||||
'<span class="eyebrow">' + (isReturning ? 'RE-ONBOARDING' : 'ONBOARDING') + ' · ' + allCompleteCount + ' av ' + ONBOARDING_GROUPS.length + ' grupper komplette</span>' +
|
title: isReturning ? 'Oppdater fellesfeltene' : 'Velkommen — la oss sette opp llm-security for ' + (orgName || 'din virksomhet'),
|
||||||
'<h1>' + (isReturning ? 'Oppdater fellesfeltene' : 'Velkommen — la oss sette opp llm-security for ' + (orgName || 'din virksomhet')) + '</h1>' +
|
lede: 'Disse 5 gruppene er felles state. De forhåndsutfyller alle command-skjemaer for nye prosjekter, så du slipper å re-skrive samme info.',
|
||||||
'<p>Disse 5 gruppene er felles state. De forhåndsutfyller alle command-skjemaer for nye prosjekter, så du slipper å re-skrive samme info.</p>' +
|
meta: ['Gruppe ' + (ONBOARDING_GROUPS.findIndex(function (g) { return g.id === group.id; }) + 1) + ' av ' + ONBOARDING_GROUPS.length, group.title || group.id]
|
||||||
'</header>'
|
}, '');
|
||||||
);
|
|
||||||
|
|
||||||
const stepNavHtml = (
|
const stepNavHtml = (
|
||||||
'<div class="onboarding-actions">' +
|
'<div class="onboarding-actions">' +
|
||||||
|
|
@ -6880,6 +6803,12 @@
|
||||||
? 'Velg arbeidsspor eller utforsk eksisterende prosjekter. Felles state er aktiv og forhåndsutfyller skjemaer.'
|
? 'Velg arbeidsspor eller utforsk eksisterende prosjekter. Felles state er aktiv og forhåndsutfyller skjemaer.'
|
||||||
: 'Single-file sikkerhetsskanning + auditing for Claude Code-prosjekter. Start med onboarding for å aktivere felles state.',
|
: 'Single-file sikkerhetsskanning + auditing for Claude Code-prosjekter. Start med onboarding for å aktivere felles state.',
|
||||||
verdict: 'n-a',
|
verdict: 'n-a',
|
||||||
|
hero: true,
|
||||||
|
meta: [
|
||||||
|
'Plugin v7.5.0',
|
||||||
|
projects.length + ' prosjekt' + (projects.length === 1 ? '' : 'er'),
|
||||||
|
CATALOG.commands.length + ' kommandoer'
|
||||||
|
],
|
||||||
keyStats: [
|
keyStats: [
|
||||||
{ label: 'PROSJEKTER', value: projects.length },
|
{ label: 'PROSJEKTER', value: projects.length },
|
||||||
{ label: 'AKTIVE RAPPORTER', value: activeReportCount },
|
{ label: 'AKTIVE RAPPORTER', value: activeReportCount },
|
||||||
|
|
@ -6977,6 +6906,11 @@
|
||||||
title: 'Command-katalog',
|
title: 'Command-katalog',
|
||||||
lede: 'Alle ' + total + ' kommandoer gruppert på kategori. Bygg pipeline-strenger uten et aktivt prosjekt.',
|
lede: 'Alle ' + total + ' kommandoer gruppert på kategori. Bygg pipeline-strenger uten et aktivt prosjekt.',
|
||||||
verdict: 'n-a',
|
verdict: 'n-a',
|
||||||
|
meta: [
|
||||||
|
total + ' kommandoer',
|
||||||
|
reportCount + ' rapport-produserende',
|
||||||
|
toolCount + ' verktøy'
|
||||||
|
],
|
||||||
keyStats: [
|
keyStats: [
|
||||||
{ label: 'TOTALT', value: total },
|
{ label: 'TOTALT', value: total },
|
||||||
{ label: 'RAPPORT-KOMMANDOER', value: reportCount },
|
{ label: 'RAPPORT-KOMMANDOER', value: reportCount },
|
||||||
|
|
@ -7159,6 +7093,11 @@
|
||||||
title: project.name,
|
title: project.name,
|
||||||
lede: project.description || '',
|
lede: project.description || '',
|
||||||
verdict: inferProjectVerdict(project),
|
verdict: inferProjectVerdict(project),
|
||||||
|
meta: [
|
||||||
|
'Target: ' + (project.target || project.target_path || '—'),
|
||||||
|
'Sist oppdatert: ' + inferProjectLastUpdated(project),
|
||||||
|
(project.scenario || project.template || 'standard')
|
||||||
|
],
|
||||||
keyStats: [
|
keyStats: [
|
||||||
{ label: 'RAPPORTER', value: reportFilled + '/' + reportTotal },
|
{ label: 'RAPPORTER', value: reportFilled + '/' + reportTotal },
|
||||||
{ label: 'SIST OPPDATERT', value: inferProjectLastUpdated(project) },
|
{ label: 'SIST OPPDATERT', value: inferProjectLastUpdated(project) },
|
||||||
|
|
@ -7206,17 +7145,30 @@
|
||||||
return '<div class="key-stats">' + items + '</div>';
|
return '<div class="key-stats">' + items + '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render page-shell — DS Tier 3 page__header-klyngen brukt på alle 4 overflater:
|
||||||
|
* - onboarding: page__eyebrow="ONBOARDING · n av 5 grupper komplette"
|
||||||
|
* - home: page__eyebrow="HJEM" (m/ hero-modifier for editorial type-hierarki)
|
||||||
|
* - catalog: page__eyebrow="KATALOG"
|
||||||
|
* - project: page__eyebrow="PROSJEKT · <TARGET>"
|
||||||
|
* Pluss alle 18 rapport-renderere (eyebrow per archetype).
|
||||||
|
* opts: { eyebrow, title, lede, meta:[], verdict, hero, keyStats }
|
||||||
|
*/
|
||||||
function renderPageShell(opts, bodyHtml) {
|
function renderPageShell(opts, bodyHtml) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
const eyebrow = opts.eyebrow ? '<span class="page__eyebrow">' + escapeHtml(opts.eyebrow) + '</span>' : '';
|
const eyebrow = opts.eyebrow ? '<span class="page__eyebrow">' + escapeHtml(opts.eyebrow) + '</span>' : '';
|
||||||
const title = '<h1 class="page__title">' + escapeHtml(opts.title || '') + '</h1>';
|
const title = '<h1 class="page__title">' + escapeHtml(opts.title || '') + '</h1>';
|
||||||
const lede = opts.lede ? '<p class="page__lede">' + escapeHtml(opts.lede) + '</p>' : '';
|
const lede = opts.lede ? '<p class="page__lede">' + escapeHtml(opts.lede) + '</p>' : '';
|
||||||
|
const meta = (opts.meta && opts.meta.length)
|
||||||
|
? '<div class="page__meta">' + opts.meta.map(function (m) { return '<span>' + escapeHtml(m) + '</span>'; }).join('') + '</div>'
|
||||||
|
: '';
|
||||||
const verdict = (opts.verdict && opts.verdict !== 'n-a') ? renderVerdictPill(opts.verdict) : '';
|
const verdict = (opts.verdict && opts.verdict !== 'n-a') ? renderVerdictPill(opts.verdict) : '';
|
||||||
const aside = verdict ? '<div class="page__header-aside">' + verdict + '</div>' : '';
|
const aside = verdict ? '<div class="page__header-aside">' + verdict + '</div>' : '';
|
||||||
const stats = renderKeyStatsGrid(opts.keyStats);
|
const stats = renderKeyStatsGrid(opts.keyStats);
|
||||||
|
const heroClass = opts.hero ? ' page__header--hero' : '';
|
||||||
return (
|
return (
|
||||||
'<header class="page__header">' +
|
'<header class="page__header' + heroClass + '">' +
|
||||||
'<div class="page__header-main">' + eyebrow + title + lede + '</div>' +
|
'<div class="page__header-main">' + eyebrow + title + lede + meta + '</div>' +
|
||||||
aside +
|
aside +
|
||||||
'</header>' +
|
'</header>' +
|
||||||
stats +
|
stats +
|
||||||
|
|
@ -7317,13 +7269,13 @@
|
||||||
const high = fs.filter(function (f) { return /^high|^høy/i.test(f.severity || ''); }).length;
|
const high = fs.filter(function (f) { return /^high|^høy/i.test(f.severity || ''); }).length;
|
||||||
return [
|
return [
|
||||||
{ label: 'TOTALT', value: fs.length },
|
{ label: 'TOTALT', value: fs.length },
|
||||||
{ label: 'KRITISK', value: crit, modifier: crit > 0 ? 'crit' : null },
|
{ label: 'KRITISK', value: crit, modifier: crit > 0 ? 'critical' : null },
|
||||||
{ label: 'HØY', value: high, modifier: high > 0 ? 'high' : null }
|
{ label: 'HØY', value: high, modifier: high > 0 ? 'high' : null }
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
'findings-grade': function (d) {
|
'findings-grade': function (d) {
|
||||||
const out = [];
|
const out = [];
|
||||||
if (d.grade) out.push({ label: 'GRADE', value: String(d.grade).toUpperCase(), modifier: /a|b/i.test(d.grade) ? 'low' : (/c|d/i.test(d.grade) ? 'med' : 'crit') });
|
if (d.grade) out.push({ label: 'GRADE', value: String(d.grade).toUpperCase(), modifier: /a|b/i.test(d.grade) ? 'low' : (/c|d/i.test(d.grade) ? 'medium' : 'critical') });
|
||||||
if (d.score != null) out.push({ label: 'SCORE', value: d.score });
|
if (d.score != null) out.push({ label: 'SCORE', value: d.score });
|
||||||
if (d.findings) out.push({ label: 'FUNN', value: d.findings.length });
|
if (d.findings) out.push({ label: 'FUNN', value: d.findings.length });
|
||||||
return out;
|
return out;
|
||||||
|
|
@ -7331,7 +7283,7 @@
|
||||||
'risk-score-meter': function (d) {
|
'risk-score-meter': function (d) {
|
||||||
const out = [];
|
const out = [];
|
||||||
if (d.risk_score != null) {
|
if (d.risk_score != null) {
|
||||||
const mod = d.risk_score >= 65 ? 'crit' : (d.risk_score >= 15 ? 'med' : 'low');
|
const mod = d.risk_score >= 65 ? 'critical' : (d.risk_score >= 15 ? 'medium' : 'low');
|
||||||
out.push({ label: 'RISK SCORE', value: d.risk_score, modifier: mod });
|
out.push({ label: 'RISK SCORE', value: d.risk_score, modifier: mod });
|
||||||
}
|
}
|
||||||
if (d.riskBand) out.push({ label: 'BAND', value: d.riskBand });
|
if (d.riskBand) out.push({ label: 'BAND', value: d.riskBand });
|
||||||
|
|
@ -7341,7 +7293,7 @@
|
||||||
return [
|
return [
|
||||||
{ label: 'TOTALT', value: d.total || 0 },
|
{ label: 'TOTALT', value: d.total || 0 },
|
||||||
{ label: 'PASS', value: d.pass_count || 0, modifier: 'low' },
|
{ label: 'PASS', value: d.pass_count || 0, modifier: 'low' },
|
||||||
{ label: 'FAIL', value: d.fail_count || 0, modifier: (d.fail_count > 0 ? 'crit' : null) }
|
{ label: 'FAIL', value: d.fail_count || 0, modifier: (d.fail_count > 0 ? 'critical' : null) }
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
'dashboard-fleet': function (d) {
|
'dashboard-fleet': function (d) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue