diff --git a/rules/patterns/web-visual-baseline/README.md b/rules/patterns/web-visual-baseline/README.md new file mode 100644 index 0000000..939792f --- /dev/null +++ b/rules/patterns/web-visual-baseline/README.md @@ -0,0 +1,222 @@ +# Web Visual Baseline — Starter Assets + +This file keeps copyable starter code. The normative rules live in `contract.md`. + +Canonical reference files (full, production version of the style) are in `assets/`: + +- `assets/view.css` +- `assets/view.html` +- `assets/upload.html` + +Prefer copying `assets/view.css` directly and adapting tokens. The starter below is a +minimal distilled subset for very small apps. + +## Copyable Starter CSS + +Use this as the default starting point for new web apps: + +```css +:root { + --bg: #ffffff; + --surface: #ffffff; + --surface-2: #f9fafb; + --border: rgba(34, 36, 38, 0.15); + --border-lite: rgba(34, 36, 38, 0.1); + --ink: rgba(0, 0, 0, 0.87); + --muted: rgba(0, 0, 0, 0.6); + --accent: #2185d0; + --accent-dark: #1678c2; + --accent-bg: #dff0ff; + --ok: #16ab39; + --warn: #f2711c; + --crit: #db2828; + --header-bg: #1b1c1d; + --radius: 4px; + --shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15); + --content-width: 1500px; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background: var(--bg); + color: var(--ink); + font: 14px/1.5 Lato, "Helvetica Neue", Arial, Helvetica, sans-serif; +} + +.page-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 14px 24px; + background: var(--header-bg); +} + +.page-header h1 { + margin: 0; + font-size: 18px; + font-weight: 700; + color: rgba(255, 255, 255, 0.9); +} + +.page-main { + width: min(var(--content-width), calc(100vw - 48px)); + margin: 28px auto 56px; +} + +.panel { + margin-bottom: 28px; + overflow: hidden; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + box-shadow: var(--shadow); +} + +.panel > h2 { + margin: 0; + padding: 13px 16px; + background: var(--surface-2); + border-bottom: 1px solid var(--border); + font-size: 13px; + font-weight: 700; +} + +.table-wrap { + overflow-x: auto; +} + +.kv-table, +.data-table { + width: 100%; + border-collapse: collapse; + background: var(--surface); +} + +.kv-table th, +.kv-table td, +.data-table th, +.data-table td { + padding: 11px 14px; + text-align: left; + vertical-align: top; + border-top: 1px solid var(--border-lite); +} + +.kv-table th, +.data-table th { + background: var(--surface-2); + border-top: 0; + border-bottom: 1px solid var(--border-lite); + font-weight: 700; + white-space: nowrap; +} + +.data-table tbody tr:hover { + background: rgba(0, 0, 0, 0.04); +} + +.button-primary { + display: inline-block; + padding: 8px 18px; + border: none; + border-radius: var(--radius); + background: var(--accent); + color: #fff; + font: inherit; + font-weight: 700; + text-decoration: none; + cursor: pointer; +} + +.button-primary:hover { + background: var(--accent-dark); +} + +.header-action { + display: inline-block; + padding: 6px 14px; + border-radius: var(--radius); + background: rgba(255, 255, 255, 0.12); + color: rgba(255, 255, 255, 0.85); + text-decoration: none; + font-size: 13px; + font-weight: 700; +} + +.header-action:hover { + background: rgba(255, 255, 255, 0.2); +} + +.status-ok { color: var(--ok); } +.status-warning { color: var(--warn); } +.status-critical { color: var(--crit); } +.status-unknown { color: rgba(0, 0, 0, 0.45); } + +@media (max-width: 720px) { + .page-header { + flex-direction: column; + padding: 12px 16px; + } + + .page-main { + width: calc(100vw - 24px); + margin-top: 20px; + } +} +``` + +## Copyable Starter HTML + +```html + + +
+
+

Overview

+
+ + + + + + + + + + + +
Hostserver-01
StatusOK
+
+
+ +
+

Devices

+
+ + + + + + + + + + + + + + + +
NameVendorStatus
NIC 1IntelWarning
+
+
+
+``` diff --git a/rules/patterns/web-visual-baseline/assets/upload.html b/rules/patterns/web-visual-baseline/assets/upload.html new file mode 100644 index 0000000..3c91724 --- /dev/null +++ b/rules/patterns/web-visual-baseline/assets/upload.html @@ -0,0 +1,35 @@ + + + + + + {{ .Title }} + + + + + +
+
+

Open Snapshot

+

Select a Reanimator JSON snapshot to render.

+
+ +
+ +
+
+ {{ if .Error }} +
{{ .Error }}
+ {{ end }} +
+
+ + diff --git a/rules/patterns/web-visual-baseline/assets/view.css b/rules/patterns/web-visual-baseline/assets/view.css new file mode 100644 index 0000000..ab8baa2 --- /dev/null +++ b/rules/patterns/web-visual-baseline/assets/view.css @@ -0,0 +1,476 @@ +:root { + --bg: #ffffff; + --surface: #ffffff; + --surface-2: #f9fafb; + --border: rgba(34, 36, 38, 0.15); + --border-lite: rgba(34, 36, 38, 0.1); + --ink: rgba(0, 0, 0, 0.87); + --muted: rgba(0, 0, 0, 0.6); + --accent: #2185d0; + --accent-dark: #1678c2; + --accent-bg: #dff0ff; + --crit-border: #e0b4b4; + --ok-bg: #fcfff5; --ok-fg: #2c662d; + --warn-bg: #fffaf3; --warn-fg: #573a08; + --crit-bg: #fff6f6; --crit-fg: #9f3a38; + --unknown-bg: #f9fafb; --unknown-fg: rgba(0, 0, 0, 0.5); + --empty-bg: #f9fafb; --empty-fg: rgba(0, 0, 0, 0.4); +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background: var(--bg); + color: var(--ink); + font: 14px/1.5 Lato, "Helvetica Neue", Arial, Helvetica, sans-serif; +} + +/* ── Header ──────────────────────────────────────── */ + +.page-header { + background: #1b1c1d; + padding: 14px 24px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; +} + +.page-header h1 { + margin: 0; + font-size: 18px; + font-weight: 700; + color: rgba(255, 255, 255, 0.9); +} + +/* ── Main layout ─────────────────────────────────── */ + +.header-actions { + display: flex; + align-items: center; +} + +.header-action { + display: inline-block; + text-decoration: none; + border-radius: 4px; + background: rgba(255, 255, 255, 0.12); + color: rgba(255, 255, 255, 0.85); + padding: 6px 14px; + font-size: 13px; + font-weight: 700; + white-space: nowrap; + transition: background 0.1s ease; +} + +.header-action:hover { + background: rgba(255, 255, 255, 0.2); +} + +.page-main { + width: min(1500px, calc(100vw - 48px)); + margin: 28px auto 56px; +} + +/* ── Meta-panel and upload — классические карточки ── */ + +.empty-panel, +.meta-panel, +.notice-panel, +.upload-panel { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 4px; + box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15); + overflow: hidden; + margin-bottom: 28px; +} + +.empty-panel h2, +.meta-panel h2, +.notice-panel h2, +.upload-panel h2 { + display: block; + margin: 0; + padding: 13px 16px; + background: var(--surface-2); + border-bottom: 1px solid var(--border); + font-size: 13px; + font-weight: 700; + color: var(--ink); +} + +.empty-panel p, +.notice-panel p, +.upload-panel p { + margin: 0; + padding: 12px 16px 0; + color: var(--muted); +} + +.empty-panel p:last-child { + padding-bottom: 16px; +} + +/* ── Section cards — heading + table, без обёртки ─── */ + +.section-card { + background: transparent; + border: none; + box-shadow: none; + overflow: visible; + margin-bottom: 32px; +} + +.section-card h2 { + display: block; + margin: 0 0 10px; + padding: 0; + background: transparent; + border: none; + font-size: 18px; + font-weight: 700; + color: rgba(0, 0, 0, 0.87); +} + +/* таблица внутри section-card получает свой бордер */ +.section-card .table-wrap { + border: 1px solid var(--border); + border-radius: 4px; + box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15); + overflow-x: auto; +} + +.section-card .kv-table { + border: 1px solid var(--border); + border-radius: 4px; + box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15); +} + +/* ── Upload ──────────────────────────────────────── */ + +.upload-panel { + width: min(520px, 100%); + margin-left: auto; + margin-right: auto; +} + +.upload-dropzone { + display: block; + margin: 12px 16px 0; + border: 1px dashed var(--border); + border-radius: 4px; + padding: 16px; + background: var(--surface-2); + cursor: pointer; + transition: border-color 0.1s ease, background 0.1s ease; +} + +.upload-dropzone:hover { + border-color: var(--accent); + background: var(--accent-bg); +} + +.upload-dropzone input { + display: block; + width: 100%; + margin-bottom: 12px; + font: inherit; + color: var(--ink); +} + +.upload-eyebrow { + display: block; + margin-bottom: 4px; + color: var(--accent); + font-size: 11px; + font-weight: 700; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +.upload-dropzone strong { + display: block; + margin-bottom: 3px; + font-size: 14px; + font-weight: 700; + color: var(--ink); +} + +.upload-dropzone span:last-child { + color: var(--muted); + font-size: 13px; +} + +.upload-actions { + padding: 12px 16px 16px; +} + +.upload-actions button { + background: var(--accent); + color: #fff; + border: none; + border-radius: 4px; + padding: 8px 18px; + font: inherit; + font-weight: 700; + cursor: pointer; + transition: background 0.1s ease; +} + +.upload-actions button:hover { + background: var(--accent-dark); +} + +.upload-actions button:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +/* ── Error ───────────────────────────────────────── */ + +.error-box { + margin: 12px 16px; + border: 1px solid var(--crit-border); + border-radius: 4px; + padding: 10px 14px; + background: var(--crit-bg); + color: var(--crit-fg); +} + +/* ── Sections grid ───────────────────────────────── */ + +.sections-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0 32px; +} + +.section-card-half { grid-column: span 1; } +.section-card-full { grid-column: 1 / -1; } + +/* ── Tables ──────────────────────────────────────── */ + +.kv-table, +.data-table { + width: 100%; + border-collapse: collapse; + font-size: 14px; + background: var(--surface); +} + +.kv-table th, +.kv-table td, +.data-table th, +.data-table td { + vertical-align: top; + text-align: left; + border-top: 1px solid var(--border-lite); + padding: 11px 14px; +} + +.kv-table tr:first-child th, +.kv-table tr:first-child td, +.data-table tr:first-child th, +.data-table tr:first-child td { + border-top: 0; +} + +.kv-table th, +.data-table th { + background: var(--surface-2); + color: var(--ink); + font-weight: 700; + white-space: nowrap; + border-bottom: 1px solid var(--border-lite); + border-top: 0; +} + +.kv-table th { + width: 1%; +} + +.data-table tbody tr:hover { + background: rgba(0, 0, 0, 0.04); + transition: background 0.1s ease; +} + +/* table-wrap уже получил border в .section-card .table-wrap */ +.table-wrap { + overflow-x: auto; +} + +/* для meta-panel table-wrap без дублирования бордера */ +.meta-panel .table-wrap { + border: none; + box-shadow: none; + border-radius: 0; +} + +.table-group + .table-group { + border-top: 1px solid var(--border); +} + +.table-group h3 { + margin: 0; + padding: 9px 14px; + color: var(--muted); + font-size: 11px; + font-weight: 700; + letter-spacing: 0.05em; + text-transform: uppercase; + background: var(--surface-2); + border-bottom: 1px solid var(--border-lite); +} + +.table-block { + display: block; +} + +.table-filter-empty { + margin: 10px 0 0; + color: var(--muted); +} + +.filter-row th { + background: var(--surface-2); + padding: 5px 8px; + border-top: 1px solid var(--border-lite); +} + +.col-filter-text { + width: 100%; + min-width: 40px; + border: 1px solid var(--border); + border-radius: 3px; + padding: 4px 6px; + font: inherit; + font-size: 12px; + background: var(--surface); + color: var(--ink); +} + +.col-filter-text:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 1px; +} + +.col-filter-select { + width: 100%; + min-width: 40px; + border: 1px solid var(--border); + border-radius: 3px; + padding: 3px 4px; + font: inherit; + font-size: 12px; + background: var(--surface); + color: var(--ink); +} + +.data-table .status-column { + width: 1%; + white-space: nowrap; + text-align: center; + padding-left: 12px; + padding-right: 12px; +} + +/* ── Status ──────────────────────────────────────── */ + +.status-badge { + display: inline-block; + font-size: 0; + white-space: nowrap; +} + +.status-badge::before { + font-size: 15px; + font-weight: 700; + line-height: 1; +} + +.status-ok::before { content: '✓'; color: #16ab39; } +.status-warning::before { content: '!'; color: #f2711c; } +.status-critical::before { content: '✗'; color: #db2828; } +.status-unknown::before { content: '?'; color: rgba(0, 0, 0, 0.4); } +.status-empty::before { content: '–'; color: rgba(0, 0, 0, 0.3); } + +.severity-info::before { content: 'i'; color: #2185d0; } +.severity-warning::before { content: '!'; color: #f2711c; } +.severity-error::before { content: '×'; color: #db2828; } +.severity-critical::before { content: '✗'; color: #a33333; } +.severity-debug::before { content: '•'; color: rgba(0, 0, 0, 0.55); } +.severity-unknown::before { content: '?'; color: rgba(0, 0, 0, 0.4); } + +/* ── Responsive ──────────────────────────────────── */ + +@media (max-width: 720px) { + .page-header { + flex-direction: column; + } + + .page-main { + width: calc(100vw - 24px); + margin-top: 20px; + } + + .sections-grid { + grid-template-columns: 1fr; + } + + .page-header { + padding: 12px 16px; + } + + .section-card-half, + .section-card-full { + grid-column: auto; + } + + +} + +/* ── Print / PDF ──────────────────────────────────── */ + +@media print { + .page-header { + background: #fff; + color: #000; + padding: 8px 0; + } + + .header-actions { + display: none; + } + + .page-main { + width: 100%; + margin: 0; + } + + .sections-grid { + grid-template-columns: 1fr; + } + + .section-card-half, + .section-card-full { + grid-column: auto; + } + + .section-card { + break-inside: avoid; + page-break-inside: avoid; + margin-bottom: 20px; + } + + .filter-row { + display: none; + } + + .data-table tbody tr:hover { + background: transparent; + } +} diff --git a/rules/patterns/web-visual-baseline/assets/view.html b/rules/patterns/web-visual-baseline/assets/view.html new file mode 100644 index 0000000..e31f008 --- /dev/null +++ b/rules/patterns/web-visual-baseline/assets/view.html @@ -0,0 +1,165 @@ + + + + + + {{ .Title }} + + {{ if not .PrintMode }}{{ end }} + {{ if .PrintMode }}{{ end }} + + + + +
+ {{ if .NoticeTitle }} +
+

{{ .NoticeTitle }}

+

{{ .NoticeBody }}

+
+ {{ end }} + + {{ if .HasSnapshot }} +
+

Snapshot Metadata

+ + + {{ range .Meta }} + + + + + {{ end }} + +
{{ .Key }}{{ .Value }}
+
+ +
+ {{ range .Sections }} +
+

{{ .Title }}

+ + {{ if eq .Kind "object" }} + + + {{ range .Rows }} + + + + + {{ end }} + +
{{ .Key }} + {{ range joinLines .Value }} +
{{ . }}
+ {{ end }} +
+ {{ end }} + + {{ if eq .Kind "table" }} + {{ $section := . }} +
+
+ + + + {{ range .Columns }} + + {{ end }} + + + + {{ range .Items }} + + {{ $row := . }} + {{ range $section.Columns }} + + {{ $value := index $row.Cells . }} + {{ if eq . "status" }} + + {{ else if eq . "severity_icon" }} + + {{ else }} + {{ range joinLines $value }} +
{{ . }}
+ {{ end }} + {{ end }} + + {{ end }} +
+ {{ end }} + +
{{ if and (ne . "status") (ne . "severity_icon") }}{{ . }}{{ end }}
+
+ +
+ {{ end }} + + {{ if eq .Kind "grouped_tables" }} + {{ range .Groups }} +
+

{{ .Title }}

+ {{ $group := . }} +
+
+ + + + {{ range .Columns }} + + {{ end }} + + + + {{ range .Items }} + + {{ $row := . }} + {{ range $group.Columns }} + + {{ $value := index $row.Cells . }} + {{ if eq . "status" }} + + {{ else if eq . "severity_icon" }} + + {{ else }} + {{ range joinLines $value }} +
{{ . }}
+ {{ end }} + {{ end }} + + {{ end }} +
+ {{ end }} + +
{{ if and (ne . "status") (ne . "severity_icon") }}{{ . }}{{ end }}
+
+ +
+
+ {{ end }} + {{ end }} +
+ {{ end }} +
+ {{ end }} + + {{ if .Error }} + + {{ end }} + + {{ if not .HasSnapshot }} +
+

Snapshot Viewer

+

This page renders one Reanimator snapshot provided by the embedding application.

+
+ {{ end }} +
+ + diff --git a/rules/patterns/web-visual-baseline/contract.md b/rules/patterns/web-visual-baseline/contract.md index 0d643c4..82a3b40 100644 --- a/rules/patterns/web-visual-baseline/contract.md +++ b/rules/patterns/web-visual-baseline/contract.md @@ -6,13 +6,15 @@ Version: 1.0 Defines the default visual baseline for future web applications in this ecosystem. -The canonical reference is the UI style from: +This is the single visual style for the ecosystem. The canonical reference files are vendored +in this pattern: -- `/Users/mchusavitin/Documents/git/chart/web/static/view.css` -- `/Users/mchusavitin/Documents/git/chart/web/templates/view.html` -- `/Users/mchusavitin/Documents/git/chart/web/templates/upload.html` +- `assets/view.css` +- `assets/view.html` +- `assets/upload.html` When a project does not already have an established design system, use this baseline by default. +Copyable starter CSS and HTML live in `README.md` next to this contract. ## Core Direction @@ -90,213 +92,3 @@ Show text or another explicit indicator together with the color treatment. - Use `table-management` for shared table geometry and interaction seams. - Use `controls-selection` for button hierarchy, filters, and bulk selection semantics. - Pattern-specific contracts may override details only when they document the reason. - -## Copyable Starter CSS - -Use this as the default starting point for new web apps: - -```css -:root { - --bg: #ffffff; - --surface: #ffffff; - --surface-2: #f9fafb; - --border: rgba(34, 36, 38, 0.15); - --border-lite: rgba(34, 36, 38, 0.1); - --ink: rgba(0, 0, 0, 0.87); - --muted: rgba(0, 0, 0, 0.6); - --accent: #2185d0; - --accent-dark: #1678c2; - --accent-bg: #dff0ff; - --ok: #16ab39; - --warn: #f2711c; - --crit: #db2828; - --header-bg: #1b1c1d; - --radius: 4px; - --shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15); - --content-width: 1500px; -} - -* { - box-sizing: border-box; -} - -body { - margin: 0; - background: var(--bg); - color: var(--ink); - font: 14px/1.5 Lato, "Helvetica Neue", Arial, Helvetica, sans-serif; -} - -.page-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - padding: 14px 24px; - background: var(--header-bg); -} - -.page-header h1 { - margin: 0; - font-size: 18px; - font-weight: 700; - color: rgba(255, 255, 255, 0.9); -} - -.page-main { - width: min(var(--content-width), calc(100vw - 48px)); - margin: 28px auto 56px; -} - -.panel { - margin-bottom: 28px; - overflow: hidden; - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius); - box-shadow: var(--shadow); -} - -.panel > h2 { - margin: 0; - padding: 13px 16px; - background: var(--surface-2); - border-bottom: 1px solid var(--border); - font-size: 13px; - font-weight: 700; -} - -.table-wrap { - overflow-x: auto; -} - -.kv-table, -.data-table { - width: 100%; - border-collapse: collapse; - background: var(--surface); -} - -.kv-table th, -.kv-table td, -.data-table th, -.data-table td { - padding: 11px 14px; - text-align: left; - vertical-align: top; - border-top: 1px solid var(--border-lite); -} - -.kv-table th, -.data-table th { - background: var(--surface-2); - border-top: 0; - border-bottom: 1px solid var(--border-lite); - font-weight: 700; - white-space: nowrap; -} - -.data-table tbody tr:hover { - background: rgba(0, 0, 0, 0.04); -} - -.button-primary { - display: inline-block; - padding: 8px 18px; - border: none; - border-radius: var(--radius); - background: var(--accent); - color: #fff; - font: inherit; - font-weight: 700; - text-decoration: none; - cursor: pointer; -} - -.button-primary:hover { - background: var(--accent-dark); -} - -.header-action { - display: inline-block; - padding: 6px 14px; - border-radius: var(--radius); - background: rgba(255, 255, 255, 0.12); - color: rgba(255, 255, 255, 0.85); - text-decoration: none; - font-size: 13px; - font-weight: 700; -} - -.header-action:hover { - background: rgba(255, 255, 255, 0.2); -} - -.status-ok { color: var(--ok); } -.status-warning { color: var(--warn); } -.status-critical { color: var(--crit); } -.status-unknown { color: rgba(0, 0, 0, 0.45); } - -@media (max-width: 720px) { - .page-header { - flex-direction: column; - padding: 12px 16px; - } - - .page-main { - width: calc(100vw - 24px); - margin-top: 20px; - } -} -``` - -## Copyable Starter HTML - -```html - - -
-
-

Overview

-
- - - - - - - - - - - -
Hostserver-01
StatusOK
-
-
- -
-

Devices

-
- - - - - - - - - - - - - - - -
NameVendorStatus
NIC 1IntelWarning
-
-
-
-```