56f3b49725
Add the complete TUI (Terminal User Interface) specification section to docs/specification.md, covering architecture, layout, navigation, persona system, reference picker, slash commands, shell mode, session management, and keyboard shortcuts. The section includes 18 HTML-styled ASCII mockup diagrams using the Dracula color palette that render with actual colors in MkDocs. Key specification areas: - MainScreen layout with right-side collapsible sidebar (3 states: hidden, visible, fullscreen cycled by shift+tab) - Direct-to-chat launch with first-run actor selection overlay - Persona system bundling actor + arguments + project/plan scope, cycled with tab; argument presets cycled with ctrl+tab - @ reference picker with fuzzy search for projects/plans/resources - / slash commands mirroring CLI noun:verb patterns (session:new, plan:list, project:show, actor:switch, etc.) - ! shell mode for direct command execution - Multi-session tabs with independent personas and conversations - Escape-cascading navigation back to main screen - Plan detail view with decision tree navigation - Project detail view with resource DAG - Permission approval workflow with diff view - Complete keyboard shortcut reference table New ADR files: - ADR-044: TUI Architecture and Framework (Textual >= 1.0) - ADR-045: TUI Persona System - ADR-046: TUI Reference and Command System (CLI-aligned commands) Also adds TUI mockup CSS classes to docs/stylesheets/extra.css for Dracula-palette diagram rendering (.tui-primary, .tui-secondary, .tui-success, .tui-warning, .tui-error, .tui-dim, .tui-bold, .tui-bold-primary, .tui-bold-warning, .tui-primary-u, .tui-rainbow).
980 lines
29 KiB
CSS
980 lines
29 KiB
CSS
/* --- Wider layout: expand page grid + sidebars -------------------------
|
||
*
|
||
* Material defaults: .md-grid max-width 61rem, sidebar 12.1rem.
|
||
* We widen the grid to 80rem and bump sidebars to ~18rem (~50% wider)
|
||
* so the content area has more breathing room on large monitors.
|
||
* ----------------------------------------------------------------------- */
|
||
|
||
@media screen and (min-width: 76.25em) {
|
||
.md-grid {
|
||
max-width: 80rem;
|
||
}
|
||
|
||
.md-sidebar {
|
||
width: 18.1rem;
|
||
}
|
||
|
||
/* Primary sidebar: match the new width everywhere Material hard-codes
|
||
the old 12.1rem value (transforms, offsets, left/right). */
|
||
.md-sidebar--primary {
|
||
width: 18.1rem;
|
||
}
|
||
|
||
/* Material sets padding-right: calc(100% - 11.5rem) on the sidebar
|
||
inner wrapper (in @supports ::-webkit-scrollbar) to reserve space
|
||
for the scrollbar gutter. 11.5rem = old 12.1rem sidebar − 0.6rem.
|
||
Update to 17.5rem = 18.1rem − 0.6rem so content fills the wider
|
||
sidebar instead of being squeezed to the old width. */
|
||
.md-sidebar__inner {
|
||
padding-right: calc(100% - 17.5rem) !important;
|
||
}
|
||
|
||
[dir=rtl] .md-sidebar__inner {
|
||
padding-left: calc(100% - 17.5rem) !important;
|
||
padding-right: 0 !important;
|
||
}
|
||
}
|
||
|
||
/* On slightly smaller desktops (60-76em), use a moderate widening so the
|
||
layout doesn't jump straight from 61rem to cramped sidebars. */
|
||
@media screen and (min-width: 60em) and (max-width: 76.1875em) {
|
||
.md-grid {
|
||
max-width: 72rem;
|
||
}
|
||
}
|
||
|
||
.md-typeset .highlight,
|
||
.md-typeset .highlight pre,
|
||
.md-typeset .highlight code {
|
||
background-color: #000;
|
||
color: #fff;
|
||
}
|
||
|
||
/* --- Section heading hierarchy -------------------------------------------
|
||
*
|
||
* Goals:
|
||
* - Every heading level is noticeably larger than its body text.
|
||
* - Higher-level headings are progressively larger than lower ones.
|
||
* - H1 is prominent but not oversized.
|
||
* - Lower-level headings (H4-H6) are bold/italic so they still read as
|
||
* headings even though their absolute size is smaller.
|
||
*
|
||
* Base body text in .md-typeset is 0.8 rem (~12.8 px).
|
||
* The scale below keeps every heading clearly above that baseline while
|
||
* maintaining a visible step between adjacent levels.
|
||
* ----------------------------------------------------------------------- */
|
||
|
||
.md-typeset h1 {
|
||
font-size: 2em; /* ~25.6 px */
|
||
font-weight: 700;
|
||
letter-spacing: -0.02em;
|
||
line-height: 1.25;
|
||
margin: 0 0 0.75em;
|
||
color: var(--md-default-fg-color);
|
||
}
|
||
|
||
.md-typeset h2 {
|
||
font-size: 1.7em; /* ~21.8 px */
|
||
font-weight: 600;
|
||
letter-spacing: -0.015em;
|
||
line-height: 1.3;
|
||
margin: 1.4em 0 0.6em;
|
||
border-bottom: 1px solid var(--md-default-fg-color--lightest);
|
||
padding-bottom: 0.3em;
|
||
}
|
||
|
||
.md-typeset h3 {
|
||
font-size: 1.4em; /* ~17.9 px */
|
||
font-weight: 600;
|
||
letter-spacing: -0.01em;
|
||
line-height: 1.35;
|
||
margin: 1.4em 0 0.6em;
|
||
border-bottom: 1px solid var(--md-default-fg-color--lightest);
|
||
padding-bottom: 0.3em;
|
||
}
|
||
|
||
.md-typeset h4 {
|
||
font-size: 1.2em; /* ~15.4 px */
|
||
font-weight: 700;
|
||
line-height: 1.4;
|
||
margin: 1.2em 0 0.5em;
|
||
border-bottom: 1px solid var(--md-default-fg-color--lightest);
|
||
padding-bottom: 0.3em;
|
||
}
|
||
|
||
.md-typeset h5 {
|
||
font-size: 1.15em; /* ~14.7 px */
|
||
font-weight: 700;
|
||
font-style: italic;
|
||
text-transform: none;
|
||
line-height: 1.4;
|
||
margin: 1.2em 0 0.4em;
|
||
border-bottom: 1px solid var(--md-default-fg-color--lightest);
|
||
padding-bottom: 0.3em;
|
||
}
|
||
|
||
.md-typeset h6 {
|
||
font-size: 1.1em; /* ~14.1 px */
|
||
font-weight: 700;
|
||
font-style: italic;
|
||
text-transform: none;
|
||
letter-spacing: 0.01em;
|
||
line-height: 1.4;
|
||
margin: 1.1em 0 0.4em;
|
||
border-bottom: 1px solid var(--md-default-fg-color--lightest);
|
||
padding-bottom: 0.3em;
|
||
color: var(--md-default-fg-color);
|
||
}
|
||
|
||
/* --- Compact table cells --------------------------------------------------
|
||
*
|
||
* MkDocs Material's default table padding is generous. Tighten it so
|
||
* wide tables (e.g. the automation-profile matrix) render without
|
||
* forcing column headers to word-wrap.
|
||
* ----------------------------------------------------------------------- */
|
||
|
||
/* First column never wraps — sized to its content. */
|
||
.md-typeset table:not([class]) th:first-child,
|
||
.md-typeset table:not([class]) td:first-child {
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.md-typeset table:not([class]) th {
|
||
min-width: 0;
|
||
}
|
||
|
||
/* --- Navbar dropdown menus ---------------------------------------------------
|
||
*
|
||
* Custom tabs.html renders top-level sections (Development, Reference, ADRs)
|
||
* as tabs with hover-activated dropdown menus listing their child pages.
|
||
* Single-page tabs (Specification, Timeline, FAQ) have no dropdown.
|
||
* ----------------------------------------------------------------------- */
|
||
|
||
/* Override Material's overflow/containment on the tabs bar and its list.
|
||
* Material sets overflow:auto and contain:content which clip absolutely-
|
||
* positioned children (our dropdown menus). We need visible overflow
|
||
* so the dropdowns can extend below the tab bar.
|
||
*
|
||
* Material's selectors:
|
||
* .md-tabs { overflow: auto } — (0,1,0)
|
||
* .md-tabs__list { contain: content; overflow: auto } — (0,1,0)
|
||
*
|
||
* Counter-selectors beat them by nesting under .md-header: */
|
||
.md-header .md-tabs {
|
||
overflow: visible;
|
||
}
|
||
|
||
.md-header .md-tabs .md-tabs__list {
|
||
overflow: visible;
|
||
contain: none;
|
||
}
|
||
|
||
/* Dropdown container — positioned below the tab item */
|
||
.md-tabs__item--dropdown {
|
||
position: relative;
|
||
}
|
||
|
||
.md-tabs__dropdown {
|
||
display: none;
|
||
position: absolute;
|
||
top: 100%;
|
||
left: 0;
|
||
min-width: 16rem;
|
||
max-height: 70vh;
|
||
overflow-y: auto;
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: .4rem 0;
|
||
background: var(--md-primary-fg-color);
|
||
border-radius: 0 0 .2rem .2rem;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
|
||
z-index: 100;
|
||
}
|
||
|
||
/* Show dropdown on hover and when the tab is focused within */
|
||
.md-tabs__item--dropdown:hover > .md-tabs__dropdown,
|
||
.md-tabs__item--dropdown:focus-within > .md-tabs__dropdown {
|
||
display: block;
|
||
}
|
||
|
||
/* Dropdown link items */
|
||
.md-tabs__dropdown-link {
|
||
display: block;
|
||
padding: .4rem 1rem;
|
||
color: var(--md-primary-bg-color);
|
||
font-size: .7rem;
|
||
white-space: nowrap;
|
||
text-decoration: none;
|
||
transition: background-color .15s;
|
||
}
|
||
|
||
.md-tabs__dropdown-link:hover {
|
||
background-color: rgba(255, 255, 255, 0.15);
|
||
}
|
||
|
||
.md-tabs__dropdown-link--active {
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* Nested section groups inside the dropdown */
|
||
.md-tabs__dropdown-group {
|
||
list-style: none;
|
||
}
|
||
|
||
.md-tabs__dropdown-label {
|
||
display: block;
|
||
padding: .5rem 1rem .2rem;
|
||
font-size: .6rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: .05em;
|
||
opacity: .7;
|
||
color: var(--md-primary-bg-color);
|
||
}
|
||
|
||
.md-tabs__dropdown-group > ul {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
/* Small caret indicator on tabs that have dropdowns */
|
||
.md-tabs__dropdown-caret {
|
||
display: inline-block;
|
||
width: 0;
|
||
height: 0;
|
||
margin-left: .3rem;
|
||
vertical-align: middle;
|
||
border-left: .25rem solid transparent;
|
||
border-right: .25rem solid transparent;
|
||
border-top: .25rem solid currentcolor;
|
||
opacity: .6;
|
||
}
|
||
|
||
/* --- Left sidebar: show only the TOC (desktop) --------------------------------
|
||
*
|
||
* With navigation.tabs + toc.integrate, the left sidebar contains both the
|
||
* sub-page navigation for the active tab AND the integrated TOC for the
|
||
* current page. We hide all non-TOC elements so the left sidebar displays
|
||
* only the Table of Contents.
|
||
*
|
||
* The TOC is always rendered inside <nav class="md-nav--secondary">.
|
||
* Everything else in the primary sidebar is page navigation.
|
||
*
|
||
* DESKTOP ONLY (>= 76.25em) — on mobile, the drawer navigation is used
|
||
* and must remain fully functional.
|
||
* ----------------------------------------------------------------------- */
|
||
|
||
@media screen and (min-width: 76.25em) {
|
||
/* Hide all top-level nav items in the sidebar — these are the page
|
||
* links within the active tab section (or the tab items themselves
|
||
* if the tab has no children). */
|
||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item {
|
||
display: none;
|
||
}
|
||
|
||
/* But show items on the ancestor path to the current page — these
|
||
* contain the nested .md-nav--secondary (TOC). */
|
||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item--active {
|
||
display: block;
|
||
}
|
||
|
||
/* Within active section wrappers, hide sibling page links (only the
|
||
* active child with its TOC should be visible).
|
||
*
|
||
* IMPORTANT: the :not(.md-nav--secondary) guard prevents this rule
|
||
* from also matching .md-nav--secondary (the TOC nav), whose <li>
|
||
* children are plain .md-nav__item without --active and would
|
||
* otherwise be hidden. */
|
||
.md-sidebar--primary .md-nav__item--active > .md-nav:not(.md-nav--secondary) > .md-nav__list > .md-nav__item:not(.md-nav__item--active) {
|
||
display: none;
|
||
}
|
||
|
||
/* Hide the page-link labels / headings on active items so only the
|
||
* TOC tree remains visible. This targets:
|
||
* - Section headings (<label class="md-nav__link">)
|
||
* - Page links (<a class="md-nav__link">)
|
||
* - The __toc checkbox and its label
|
||
* - The section title inside nested navs
|
||
* but NOT anything inside .md-nav--secondary (the TOC itself). */
|
||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item--active > .md-nav__toggle,
|
||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item--active > label.md-nav__link,
|
||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item--active > a.md-nav__link,
|
||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item--active > .md-nav__container,
|
||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item--active > nav > .md-nav__title {
|
||
display: none;
|
||
}
|
||
|
||
/* Within nested sections (level 2+), hide the active page's own link
|
||
* and __toc machinery, leaving only the .md-nav--secondary TOC. */
|
||
.md-sidebar--primary .md-nav__item--active > #__toc,
|
||
.md-sidebar--primary .md-nav__item--active > label[for="__toc"],
|
||
.md-sidebar--primary .md-nav__item--active > a.md-nav__link,
|
||
.md-sidebar--primary .md-nav__item--active > .md-nav__container {
|
||
display: none;
|
||
}
|
||
|
||
/* Force the TOC (md-nav--secondary) visible.
|
||
*
|
||
* Material's rules that hide or collapse the TOC:
|
||
*
|
||
* A) .md-nav--primary .md-nav__link[for=__toc]~.md-nav
|
||
* → display: none — (0,4,0)
|
||
*
|
||
* B) .md-nav__toggle~.md-nav (desktop media query)
|
||
* → display: grid; opacity: 0; visibility: collapse — (0,2,0)
|
||
*
|
||
* Material's integrated rule tries to re-show it:
|
||
* C) .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary
|
||
* → display: block; opacity: 1; visibility: visible — (0,4,0)
|
||
*
|
||
* A and C tie at (0,4,0), so cascade order decides — and A often
|
||
* wins. We counter with higher-specificity selectors that
|
||
* guarantee the TOC is always visible without using !important.
|
||
*/
|
||
|
||
/* Counter rule A — specificity (0,6,0) beats (0,4,0).
|
||
* Directly negates the sibling-combinator hiding rule. */
|
||
.md-sidebar--primary .md-nav--primary .md-nav__link[for="__toc"] ~ nav.md-nav--secondary {
|
||
display: block;
|
||
opacity: 1;
|
||
visibility: visible;
|
||
}
|
||
|
||
/* General fallback for integrated mode — specificity (0,5,0)
|
||
* beats rule B (0,2,0) and ties/beats rule C (0,4,0) by being
|
||
* loaded later. */
|
||
.md-sidebar--primary .md-nav--primary.md-nav--integrated .md-nav__item--active .md-nav--secondary {
|
||
display: block;
|
||
opacity: 1;
|
||
visibility: visible;
|
||
}
|
||
|
||
/* Remove the top-level sidebar title ("Navigation" label) on desktop
|
||
* since we no longer show page navigation in the sidebar. */
|
||
.md-sidebar--primary .md-nav--primary > .md-nav__title {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
/* --- Collapsible TOC in integrated sidebar --- */
|
||
|
||
/* Container for the link row: link text + toggle chevron side by side.
|
||
* Scoped to .md-nav--secondary (active page TOC) so it does not affect
|
||
* primary nav section headings which use Material's native <label> +
|
||
* checkbox. */
|
||
.md-nav--secondary .md-nav__item--nested > .md-nav__link {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
/* Chevron toggle button injected by JS into TOC sub-items.
|
||
* Styled to match Material's native .md-nav__icon chevron exactly so
|
||
* all expand/collapse indicators look the same across the sidebar. */
|
||
.toc-toggle {
|
||
flex-shrink: 0;
|
||
width: .9rem;
|
||
height: .9rem;
|
||
margin-left: auto;
|
||
cursor: pointer;
|
||
border: none;
|
||
background: none;
|
||
border-radius: 100%;
|
||
transition: background-color .25s;
|
||
}
|
||
|
||
.toc-toggle:hover {
|
||
background-color: var(--md-accent-fg-color--transparent);
|
||
}
|
||
|
||
/* Use the same SVG mask-image chevron as Material's .md-nav__icon::after */
|
||
.toc-toggle::after {
|
||
content: "";
|
||
display: inline-block;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: currentcolor;
|
||
border-radius: 100%;
|
||
-webkit-mask-image: var(--md-nav-icon--next);
|
||
mask-image: var(--md-nav-icon--next);
|
||
-webkit-mask-position: center;
|
||
mask-position: center;
|
||
-webkit-mask-repeat: no-repeat;
|
||
mask-repeat: no-repeat;
|
||
-webkit-mask-size: contain;
|
||
mask-size: contain;
|
||
vertical-align: -.1rem;
|
||
transition: transform .25s;
|
||
/* Default: expanded (pointing down) */
|
||
transform: rotate(90deg);
|
||
}
|
||
|
||
/* Rotate arrow to point right when collapsed */
|
||
.toc-collapsed > .md-nav__link > .toc-toggle::after {
|
||
transform: rotate(0deg);
|
||
}
|
||
|
||
/* Hide/show nested nav list when collapsed (TOC items only).
|
||
* Desktop only — on mobile the TOC is rendered within Material's drawer
|
||
* and must remain in the render tree for correct scroll behaviour. */
|
||
@media screen and (min-width: 76.25em) {
|
||
.md-nav--secondary .md-nav__item--nested.toc-collapsed > .md-nav {
|
||
display: none;
|
||
}
|
||
|
||
.md-nav--secondary .md-nav__item--nested > .md-nav {
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
/* --- Mobile sidebar scroll fix -----------------------------------------------
|
||
*
|
||
* On mobile (< 76.25em), Material renders the primary sidebar as a
|
||
* slide-out drawer. The custom toc-collapse.js script (which is
|
||
* desktop-only) restructures nav items and collapses sections, but on
|
||
* mobile we rely on Material's native drawer navigation. Ensure the
|
||
* drawer's content is scrollable when there are more items than fit on
|
||
* screen — this was broken because the desktop-only display:none rules
|
||
* were interfering with the scroll container height computation, and
|
||
* the nav containers lacked explicit overflow declarations.
|
||
* ----------------------------------------------------------------------- */
|
||
|
||
@media screen and (max-width: 76.1875em) {
|
||
/* Ensure the sidebar drawer inner wrapper scrolls vertically */
|
||
.md-sidebar--primary .md-sidebar__inner {
|
||
overflow-y: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
/* Ensure each nav level within the mobile drawer can scroll when it
|
||
* exceeds the viewport height. Material uses absolute positioning
|
||
* and transforms for the drill-down layers; this ensures each layer
|
||
* is independently scrollable. */
|
||
.md-sidebar--primary .md-nav__list {
|
||
overflow-y: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
/* Prevent the overall nav from clipping its children —
|
||
* Material's mobile drawer needs visible overflow on the
|
||
* horizontal axis for the slide animation, and auto on the
|
||
* vertical axis for scrolling. */
|
||
.md-sidebar--primary .md-nav--primary {
|
||
overflow: visible;
|
||
}
|
||
|
||
/* Undo any items hidden by the JS-driven toc-collapsed class that
|
||
* may have been applied before the mobile guard kicks in (race
|
||
* condition on resize or orientation change).
|
||
*
|
||
* The desktop hiding rule is:
|
||
* .md-nav--secondary .md-nav__item--nested.toc-collapsed > .md-nav
|
||
* { display: none } — (0,3,0), inside a ≥76.25em media query
|
||
*
|
||
* This mobile rule applies inside a <76.1875em media query, so the
|
||
* two never conflict — no !important needed. We bump specificity
|
||
* slightly for safety against any other rules. */
|
||
.md-sidebar--primary .md-nav--secondary .toc-collapsed > .md-nav {
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
/* --- Diagram lightbox (fullscreen on click) --------------------------------
|
||
*
|
||
* Kroki diagrams (<img alt="Kroki">) get a pointer cursor to hint they are
|
||
* clickable. Clicking one opens a fullscreen overlay with the diagram
|
||
* scaled to fill the viewport. An X button or the Escape key closes it.
|
||
* ----------------------------------------------------------------------- */
|
||
|
||
/* Make diagram images look clickable */
|
||
img[alt="Kroki"] {
|
||
cursor: zoom-in;
|
||
transition: opacity 0.15s ease;
|
||
}
|
||
|
||
img[alt="Kroki"]:hover {
|
||
opacity: 0.85;
|
||
}
|
||
|
||
/* Fullscreen overlay */
|
||
.diagram-lightbox {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 9999;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgba(0, 0, 0, 0.88);
|
||
backdrop-filter: blur(4px);
|
||
-webkit-backdrop-filter: blur(4px);
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
transition: opacity 0.2s ease, visibility 0.2s ease;
|
||
padding: 2rem;
|
||
}
|
||
|
||
.diagram-lightbox.active {
|
||
opacity: 1;
|
||
visibility: visible;
|
||
}
|
||
|
||
/* The enlarged diagram image */
|
||
.diagram-lightbox img {
|
||
max-width: 95vw;
|
||
max-height: 92vh;
|
||
object-fit: contain;
|
||
border-radius: 4px;
|
||
/* Invert for dark backgrounds so SVG lines stay visible */
|
||
filter: drop-shadow(0 2px 20px rgba(0, 0, 0, 0.5));
|
||
background: #fff;
|
||
padding: 1rem;
|
||
}
|
||
|
||
/* Close button */
|
||
.diagram-lightbox-close {
|
||
position: absolute;
|
||
top: 1rem;
|
||
right: 1.5rem;
|
||
width: 2.5rem;
|
||
height: 2.5rem;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgba(255, 255, 255, 0.15);
|
||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||
border-radius: 50%;
|
||
color: #fff;
|
||
font-size: 1.5rem;
|
||
line-height: 1;
|
||
cursor: pointer;
|
||
transition: background 0.15s ease, transform 0.15s ease;
|
||
z-index: 10000;
|
||
}
|
||
|
||
.diagram-lightbox-close:hover {
|
||
background: rgba(255, 255, 255, 0.3);
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
.diagram-lightbox-close:active {
|
||
transform: scale(0.95);
|
||
}
|
||
|
||
/* ── TUI Mockup Diagrams ─────────────────────────────────────────
|
||
*
|
||
* Renders TUI specification ASCII mockups as styled terminal windows
|
||
* with Dracula-inspired coloring. Span classes map to the TUI's
|
||
* semantic color tokens ($primary, $secondary, $success, etc.).
|
||
*
|
||
* Usage in markdown:
|
||
* <div class="tui-mockup"><pre>
|
||
* ┌───────────┐
|
||
* │ <span class="tui-primary">hello</span> │
|
||
* └───────────┘</pre></div>
|
||
* ----------------------------------------------------------------------- */
|
||
|
||
.tui-mockup {
|
||
margin: 1.5em 0;
|
||
}
|
||
|
||
.tui-mockup pre {
|
||
/* Dracula palette */
|
||
--tui-fg: #f8f8f2;
|
||
--tui-bg: #21222c;
|
||
--tui-primary: #8be9fd;
|
||
--tui-secondary: #bd93f9;
|
||
--tui-success: #50fa7b;
|
||
--tui-warning: #f1fa8c;
|
||
--tui-error: #ff5555;
|
||
--tui-dim: #6272a4;
|
||
--tui-border-color: #44475a;
|
||
|
||
background: var(--tui-bg) !important;
|
||
color: var(--tui-fg) !important;
|
||
font-family: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', 'Consolas', 'Monaco', monospace;
|
||
font-size: 0.72rem;
|
||
line-height: 1.45;
|
||
padding: 0.8rem 1rem;
|
||
border-radius: 6px;
|
||
border: 1px solid var(--tui-border-color);
|
||
overflow-x: auto;
|
||
white-space: pre;
|
||
margin: 0;
|
||
}
|
||
|
||
/* Semantic color spans */
|
||
.tui-mockup .tui-primary { color: var(--tui-primary); }
|
||
.tui-mockup .tui-secondary { color: var(--tui-secondary); }
|
||
.tui-mockup .tui-success { color: var(--tui-success); }
|
||
.tui-mockup .tui-warning { color: var(--tui-warning); }
|
||
.tui-mockup .tui-error { color: var(--tui-error); }
|
||
.tui-mockup .tui-dim { color: var(--tui-dim); }
|
||
.tui-mockup .tui-bold { font-weight: 700; }
|
||
.tui-mockup .tui-bold-primary { font-weight: 700; color: var(--tui-primary); }
|
||
.tui-mockup .tui-bold-warning { font-weight: 700; color: var(--tui-warning); }
|
||
.tui-mockup .tui-primary-u { color: var(--tui-primary); text-decoration: underline; text-underline-offset: 2px; }
|
||
|
||
/* Rainbow gradient text (throbber bar). Uses the exact gradient
|
||
* colors from the TUI specification's throbber section, animated
|
||
* to cycle continuously. */
|
||
.tui-mockup .tui-rainbow {
|
||
font-weight: 700;
|
||
background: linear-gradient(
|
||
90deg,
|
||
#881177, #aa3355, #cc6666, #ee9944, #eedd00,
|
||
#99dd55, #44dd88, #22ccbb, #00bbcc, #0099cc,
|
||
#3366bb, #663399, #881177
|
||
);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
background-size: 200% 100%;
|
||
animation: tui-rainbow-shift 3s linear infinite;
|
||
}
|
||
|
||
@keyframes tui-rainbow-shift {
|
||
0% { background-position: 0% 50%; }
|
||
100% { background-position: 200% 50%; }
|
||
}
|
||
|
||
/* ── ADR Admonition ──────────────────────────────────────────────── */
|
||
|
||
:root {
|
||
--md-admonition-icon--adr: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm-1 7V3.5L18.5 9H13zM6 20V4h5v7h7v9H6z"/><path d="M8 14h8v2H8zm0-3h8v2H8z"/></svg>');
|
||
}
|
||
|
||
.md-typeset .admonition.adr,
|
||
.md-typeset details.adr {
|
||
border-color: #7c4dff;
|
||
}
|
||
|
||
.md-typeset .adr > .admonition-title,
|
||
.md-typeset .adr > summary {
|
||
background-color: rgba(124, 77, 255, 0.1);
|
||
}
|
||
|
||
.md-typeset .adr > .admonition-title::before,
|
||
.md-typeset .adr > summary::before {
|
||
background-color: #7c4dff;
|
||
-webkit-mask-image: var(--md-admonition-icon--adr);
|
||
mask-image: var(--md-admonition-icon--adr);
|
||
}
|
||
|
||
/* ── ADR Page: Custom Header (subtitle style) ────────────────────── */
|
||
|
||
.adr-header {
|
||
margin: -0.4rem 0 1.2rem;
|
||
font-size: 0.82rem;
|
||
font-style: italic;
|
||
color: var(--md-default-fg-color--light);
|
||
line-height: 1.7;
|
||
}
|
||
|
||
.adr-header-line {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: baseline;
|
||
gap: 0.2rem 0.5rem;
|
||
}
|
||
|
||
.adr-header-line .adr-label {
|
||
color: var(--md-default-fg-color--light);
|
||
}
|
||
|
||
.adr-header-line .adr-value {
|
||
color: var(--md-default-fg-color);
|
||
}
|
||
|
||
.adr-header-line a {
|
||
color: var(--md-typeset-a-color);
|
||
}
|
||
|
||
.adr-header-line a:hover {
|
||
color: var(--md-accent-fg-color);
|
||
}
|
||
|
||
/* ── ADR SVG Icon (inline, matching log4brains Material icons) ───── */
|
||
|
||
.adr-icon {
|
||
display: inline-block;
|
||
width: 1em;
|
||
height: 1em;
|
||
vertical-align: -0.125em;
|
||
fill: currentColor;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* ── ADR Status Badge (log4brains MuiChip-outlined style) ────────── */
|
||
|
||
.adr-status-badge {
|
||
display: inline-block;
|
||
height: 18px;
|
||
line-height: 16px;
|
||
font-size: 0.74rem;
|
||
font-weight: 500;
|
||
font-style: normal; /* override when inside the italic header */
|
||
padding: 0 6px;
|
||
border: 1px solid currentColor;
|
||
border-radius: 9px;
|
||
white-space: nowrap;
|
||
vertical-align: text-bottom;
|
||
}
|
||
|
||
/* Status colours (matching log4brains palette) */
|
||
.adr-status-draft { color: #424242; }
|
||
.adr-status-proposed { color: #283593; }
|
||
.adr-status-accepted { color: #558b2f; }
|
||
.adr-status-rejected { color: #d84315; }
|
||
.adr-status-deprecated{ color: #6d4c41; }
|
||
.adr-status-superseded{ color: #6d4c41; }
|
||
|
||
/* Superseded / Deprecated title gets strike-through */
|
||
body.adr-page .md-content h1 {
|
||
/* default – no strike-through */
|
||
}
|
||
body.adr-page.adr-superseded .md-content h1,
|
||
body.adr-page.adr-deprecated .md-content h1 {
|
||
text-decoration: line-through;
|
||
text-decoration-color: #6d4c41;
|
||
}
|
||
|
||
/* Dark-mode adjustments */
|
||
[data-md-color-scheme="slate"] .adr-status-draft { color: #bdbdbd; }
|
||
[data-md-color-scheme="slate"] .adr-status-proposed { color: #7986cb; }
|
||
[data-md-color-scheme="slate"] .adr-status-accepted { color: #81c784; }
|
||
[data-md-color-scheme="slate"] .adr-status-rejected { color: #e57373; }
|
||
[data-md-color-scheme="slate"] .adr-status-deprecated{ color: #a1887f; }
|
||
[data-md-color-scheme="slate"] .adr-status-superseded{ color: #a1887f; }
|
||
|
||
/* ── Tier badge ──────────────────────────────────────────────────── */
|
||
|
||
.adr-tier-badge {
|
||
display: inline-block;
|
||
height: 18px;
|
||
line-height: 16px;
|
||
font-size: 0.74rem;
|
||
font-weight: 500;
|
||
font-style: normal; /* override when inside the italic header */
|
||
padding: 0 6px;
|
||
border: 1px solid var(--md-default-fg-color--lighter);
|
||
border-radius: 9px;
|
||
color: var(--md-default-fg-color--light);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.adr-tier-title {
|
||
font-size: 0.8rem;
|
||
color: var(--md-default-fg-color--light);
|
||
}
|
||
|
||
/* ── ADR Timeline (in-page, moved to sidebar by JS) ─────────────── */
|
||
|
||
.adr-timeline {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.adr-timeline-entry {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
min-height: 48px;
|
||
}
|
||
|
||
/* Left column: date */
|
||
.adr-timeline-date-col {
|
||
flex: 0 0 10ch;
|
||
text-align: right;
|
||
padding-right: 10px;
|
||
padding-top: 2px;
|
||
}
|
||
|
||
.adr-timeline-date {
|
||
font-size: 0.72rem;
|
||
color: #9e9e9e;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
[data-md-color-scheme="slate"] .adr-timeline-date {
|
||
color: #757575;
|
||
}
|
||
|
||
/* Centre column: dot + vertical connector */
|
||
.adr-timeline-connector {
|
||
flex: 0 0 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
align-self: stretch; /* span the full row height so the line fills the gap */
|
||
}
|
||
|
||
.adr-timeline-dot {
|
||
width: 12px;
|
||
height: 12px;
|
||
border-radius: 50%;
|
||
flex-shrink: 0;
|
||
margin-top: 3px;
|
||
border: 2px solid currentColor;
|
||
background: var(--md-default-bg-color, #fff);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* Dot colours re-use status palette */
|
||
.adr-dot-draft { color: #424242; border-color: #424242; }
|
||
.adr-dot-proposed { color: #283593; border-color: #283593; }
|
||
.adr-dot-accepted { color: #558b2f; border-color: #558b2f; }
|
||
.adr-dot-rejected { color: #d84315; border-color: #d84315; }
|
||
.adr-dot-deprecated { color: #6d4c41; border-color: #6d4c41; }
|
||
.adr-dot-superseded { color: #6d4c41; border-color: #6d4c41; }
|
||
|
||
/* Current (last) entry: filled dot, slightly larger */
|
||
.adr-dot-current {
|
||
width: 14px;
|
||
height: 14px;
|
||
margin-top: 2px;
|
||
background: currentColor;
|
||
}
|
||
|
||
/* Dark-mode dot colours */
|
||
[data-md-color-scheme="slate"] .adr-dot-draft { color: #bdbdbd; border-color: #bdbdbd; }
|
||
[data-md-color-scheme="slate"] .adr-dot-proposed { color: #7986cb; border-color: #7986cb; }
|
||
[data-md-color-scheme="slate"] .adr-dot-accepted { color: #81c784; border-color: #81c784; }
|
||
[data-md-color-scheme="slate"] .adr-dot-rejected { color: #e57373; border-color: #e57373; }
|
||
[data-md-color-scheme="slate"] .adr-dot-deprecated { color: #a1887f; border-color: #a1887f; }
|
||
[data-md-color-scheme="slate"] .adr-dot-superseded { color: #a1887f; border-color: #a1887f; }
|
||
|
||
/* Connector line between dots */
|
||
.adr-timeline-line {
|
||
width: 2px;
|
||
flex-grow: 1;
|
||
min-height: 16px;
|
||
background: #e0e0e0;
|
||
}
|
||
|
||
[data-md-color-scheme="slate"] .adr-timeline-line {
|
||
background: #424242;
|
||
}
|
||
|
||
/* Right column: status + person */
|
||
.adr-timeline-content {
|
||
flex: 1 1 auto;
|
||
padding-left: 10px;
|
||
padding-bottom: 12px;
|
||
}
|
||
|
||
.adr-timeline-content .adr-status-badge {
|
||
display: inline-block;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.adr-timeline-person {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.3rem;
|
||
font-size: 0.72rem;
|
||
color: #616161;
|
||
margin-top: 3px;
|
||
}
|
||
|
||
[data-md-color-scheme="slate"] .adr-timeline-person {
|
||
color: #9e9e9e;
|
||
}
|
||
|
||
.adr-timeline-person .adr-icon {
|
||
font-size: 0.85em;
|
||
}
|
||
|
||
/* ── Sidebar timeline wrapper (set by adr-page.js) ──────────────── */
|
||
|
||
.adr-sidebar-timeline {
|
||
padding: 0.6rem 0.6rem 1rem;
|
||
}
|
||
|
||
.adr-timeline-title {
|
||
display: block;
|
||
font-size: 0.7rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
color: var(--md-default-fg-color--light);
|
||
padding-bottom: 0.5rem;
|
||
margin-bottom: 0.25rem;
|
||
border-bottom: 1px solid var(--md-default-fg-color--lightest);
|
||
}
|
||
|
||
/* When in the sidebar, tighten the timeline dimensions */
|
||
.adr-sidebar-timeline .adr-timeline-date-col {
|
||
flex: 0 0 8ch;
|
||
padding-right: 6px;
|
||
}
|
||
|
||
.adr-sidebar-timeline .adr-timeline-entry {
|
||
min-height: 40px;
|
||
}
|
||
|
||
.adr-sidebar-timeline .adr-timeline-content {
|
||
padding-left: 8px;
|
||
padding-bottom: 10px;
|
||
}
|
||
|
||
/* ── ADR page: hide the integrated TOC in the left sidebar ───────── */
|
||
/* Desktop only — the JS replaces the sidebar, but in case it hasn't
|
||
run yet or on print, also hide the secondary nav (TOC items). */
|
||
|
||
@media screen and (min-width: 76.25em) {
|
||
body.adr-page .md-sidebar--primary .md-nav--secondary {
|
||
display: none;
|
||
}
|
||
|
||
/* Ensure the JS-injected timeline overrides the nav-hiding CSS. */
|
||
body.adr-page .md-sidebar--primary .adr-sidebar-timeline {
|
||
display: block !important;
|
||
}
|
||
}
|
||
|
||
/* ── ADR page: mobile timeline (appended at end of article by JS) ── */
|
||
|
||
.adr-timeline-mobile {
|
||
margin-top: 2.5rem;
|
||
padding-top: 1.5rem;
|
||
border-top: 1px solid var(--md-default-fg-color--lightest);
|
||
}
|
||
|
||
/* ── Combined ADR timeline (index page, always visible in body) ──── */
|
||
|
||
.adr-combined-timeline {
|
||
display: flex !important; /* override the display:none on .adr-timeline */
|
||
flex-direction: column;
|
||
margin-top: 1rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
/* ADR link row (replaces the person row on individual timelines) */
|
||
.adr-timeline-adr-link {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.3rem;
|
||
font-size: 0.72rem;
|
||
color: #616161;
|
||
margin-top: 3px;
|
||
}
|
||
|
||
[data-md-color-scheme="slate"] .adr-timeline-adr-link {
|
||
color: #9e9e9e;
|
||
}
|
||
|
||
.adr-timeline-adr-link .adr-icon {
|
||
width: 0.85em;
|
||
height: 0.85em;
|
||
flex-shrink: 0;
|
||
fill: currentColor;
|
||
}
|
||
|
||
.adr-timeline-adr-link a {
|
||
color: var(--md-typeset-a-color);
|
||
text-decoration: none;
|
||
}
|
||
|
||
.adr-timeline-adr-link a:hover {
|
||
text-decoration: underline;
|
||
}
|