cookcli 0.38.1

A command-line interface for managing and working with Cooklang recipes
Documentation
@import "tailwindcss" source(none);

/* Tailwind v4 is CSS-first. The two things tailwind.config.js used to own —
   class-based dark mode and the list of files to scan for class names — are
   declared here instead. `source(none)` disables Tailwind's automatic
   whole-repo auto-detection, so only the paths named below are scanned. */
@custom-variant dark (&:where(.dark, .dark *));

@source "../../templates";
@source "../../static/js/*.js";
@source "../../static/js/src";
@source not "../../static/js/editor.bundle.js";
/* The one Rust file that emits class names: method_classes() for the API
   docs method badge. */
@source "../../src/web/templates.rs";

/* ============================================================
   DESIGN TOKENS
   Twenty-one semantic colour names plus radii and shadows, defined once per
   theme. Every colour in the UI resolves through one of these. Do not
   introduce raw hex values or Tailwind palette utilities in templates.
   --accent-strong is the fill for controls that carry text: the DS orange
   (--accent) only reaches 3.7:1 under white text, so filled buttons use
   this slightly darker step, where white (--accent-ink) clears AA at 4.7:1.
   --accent itself stays on icons, borders, focus rings and status dots.
   ============================================================ */
:root {
    color-scheme: light;

    --bg:            #fcfcfb;
    --surface:       #ffffff;
    --surface-sunk:  #f5f3f0;
    --border:        #e4e0da;
    --border-strong: #c3bcb1;
    --text:          #16161d;   /* DS Text/Primary */
    --text-muted:    #5f5a51;   /* DS Text/Secondary, darkened for AA */
    --text-faint:    #6a645b;
    --accent:        #e15a29;   /* DS Controls/Primary, Icons/Primary */
    --accent-strong: #c94a1c;   /* --accent darkened until white text clears AA */
    --accent-text:   #715329;   /* DS Text/Tags */
    --accent-soft:   #f5dacf;   /* DS Background/UI One */
    --accent-ink:    #ffffff;
    --ok:            #3d6849;
    --ok-soft:       #e2e8df;
    --danger:        #c4261c;   /* DS Text/Warning, darkened for AA */
    --danger-soft:   #f7dfdc;
    --danger-ink:    #ffffff;
    /* Links. The design system is entirely warm, so this is the dark end of
       the DS orange rather than a blue; every link also carries an underline
       so the affordance never rests on hue alone. */
    --info:          #8a3d14;
    --disabled:      #d3cdcb;   /* DS Controls/Disabled */
    --inactive:      #8a8075;   /* DS Controls/Inactive, darkened for AA */

    --radius-control: 6px;
    --radius-card:    6px;

    /* Two elevations. In-flow surfaces get --shadow-card, which is barely a
       shadow at all — the border does the work. --shadow-overlay is only for
       things floating above the page: dropdowns, dialogs, search results. */
    --shadow-card:    0 1px 0 rgba(27, 31, 36, .04);
    --shadow-overlay: 0 8px 24px rgba(27, 31, 36, .12);
}

/* .cooking-overlay is listed here deliberately: cook mode is always dark
   regardless of the site theme, so its inline entity badges must resolve the
   dark token values or they render at ~2.5:1 on the dark card. */
.dark,
.cooking-overlay {
    color-scheme: dark;

    --bg:            #16161d;
    --surface:       #1c1c24;
    --surface-sunk:  #23232c;
    --border:        #30303b;
    --border-strong: #43434f;
    --text:          #efeae6;
    --text-muted:    #ada69b;
    --text-faint:    #948d83;
    --accent:        #e15a29;
    --accent-strong: #c94a1c;
    --accent-text:   #f08050;
    --accent-soft:   #3a2820;
    --accent-ink:    #ffffff;
    --ok:            #6fb283;
    --ok-soft:       #1e2a22;
    --danger:        #ff6b60;
    --danger-soft:   #2e1b1a;
    --danger-ink:    #16161d;
    --info:          #e59a6d;
    --disabled:      #4a4a55;
    --inactive:      #8f8880;

    --shadow-card:    none;
    --shadow-overlay: 0 8px 24px rgba(0, 0, 0, .5);
}

/* Register tokens with Tailwind so they generate real utilities
   (bg-surface, text-muted, border-line, …). `inline` makes the generated
   utilities reference the custom property, so they flip automatically under
   .dark with no override rules. Border tokens are named `line` so the
   utility reads `border-line` rather than `border-border`. */
@theme inline {
    --color-bg:          var(--bg);
    --color-surface:     var(--surface);
    --color-sunk:        var(--surface-sunk);
    --color-line:        var(--border);
    --color-line-strong: var(--border-strong);
    --color-text:        var(--text);
    --color-muted:       var(--text-muted);
    --color-faint:       var(--text-faint);
    --color-accent:      var(--accent);
    --color-accent-strong: var(--accent-strong);
    --color-accent-text: var(--accent-text);
    --color-accent-soft: var(--accent-soft);
    --color-accent-ink:  var(--accent-ink);
    --color-ok:          var(--ok);
    --color-ok-soft:     var(--ok-soft);
    --color-danger:      var(--danger);
    --color-danger-soft: var(--danger-soft);
    --color-danger-ink:  var(--danger-ink);
    --color-info:        var(--info);
    --color-disabled:    var(--disabled);
    --color-inactive:    var(--inactive);
}

/* ============================================================
   TYPE SCALE
   Seven steps, each with a role and a line-height chosen for that role.
   `display` is 30px — the size main's page titles have always been — the
   rest are the PR #456 scale. Tailwind's xs/sm/base/lg/2xl/3xl names are
   aliased onto the steps so a stray `text-sm` cannot drift off the scale.
   ============================================================ */
@theme {
    --text-display: 30px;
    --text-display--line-height: 1.2;
    --text-display--letter-spacing: -.01em;

    --text-title: 18px;
    --text-title--line-height: 1.35;
    --text-title--letter-spacing: -.005em;

    --text-read: 16px;
    --text-read--line-height: 1.6;

    --text-body: 14px;
    --text-body--line-height: 1.5;

    --text-ui: 13px;
    --text-ui--line-height: 1.4;

    --text-meta: 12px;
    --text-meta--line-height: 1.4;

    --text-label: 11px;
    --text-label--line-height: 1.3;
    --text-label--letter-spacing: .06em;

    --text-xs:   var(--text-meta);
    --text-xs--line-height: var(--text-meta--line-height);
    --text-sm:   var(--text-body);
    --text-sm--line-height: var(--text-body--line-height);
    --text-base: var(--text-read);
    --text-base--line-height: var(--text-read--line-height);
    --text-lg:   var(--text-title);
    --text-lg--line-height: var(--text-title--line-height);
    --text-2xl:  var(--text-display);
    --text-2xl--line-height: var(--text-display--line-height);
    --text-3xl:  var(--text-display);
    --text-3xl--line-height: var(--text-display--line-height);
}

body {
    font-size: var(--text-body);
    line-height: 1.5;
}

/* Component vocabulary. Kept in its own file so this one stays the small,
   stable theme contract. */
@import "./components.css";

/* Print: force the light token values regardless of the active theme.
   Layout flattening lives in base.html's print block. */
@media print {
    :root,
    .dark,
    .cooking-overlay {
        color-scheme: light;

        --bg:            #ffffff;
        --surface:       #ffffff;
        --surface-sunk:  #f5f4f2;
        --border:        #dddad5;
        --border-strong: #bbb6ae;
        --text:          #111111;
        --text-muted:    #444444;
        --text-faint:    #666666;
        --accent:        #a8380c;
        --accent-strong: #a8380c;
        --accent-text:   #a8380c;
        --accent-soft:   #f7ece5;
        --accent-ink:    #ffffff;
        --ok:            #23624a;
        --ok-soft:       #eaf3ee;
        --danger:        #96201a;
        --danger-soft:   #f8eae9;
        --danger-ink:    #ffffff;
        --info:          #1f4f86;
        --disabled:      #cccccc;
        --inactive:      #767676;

        --shadow-card:    none;
        --shadow-overlay: none;
    }

    .card,
    .btn,
    .stepper,
    .nav-card,
    .recipe-card,
    .step-box,
    .ingredient-row,
    .pantry-item {
        border: 1px solid var(--border) !important;
        box-shadow: none !important;
    }

    .card,
    .step-box {
        break-inside: avoid;
    }
}

/* ============================================================
   CODEMIRROR
   Token-driven in BOTH themes. These rules live OUTSIDE @layer components on
   purpose: CodeMirror injects its base theme as an unlayered <style> at
   runtime, and unlayered always beats layered regardless of specificity.
   `.cm-editor.cm-editor` reaches 0,3,0 to beat CodeMirror's own `.ͼ2 .cm-*`
   selectors in source order without !important.
   ============================================================ */
.cm-editor {
    background: var(--surface);
    color: var(--text);
}

.cm-editor.cm-editor .cm-gutters {
    background: var(--surface-sunk);
    border-color: var(--border);
    color: var(--text-faint);
}

.cm-editor.cm-editor .cm-activeLineGutter {
    background: var(--surface-sunk);
    color: var(--text-muted);
}

.cm-editor.cm-editor .cm-activeLine {
    background: color-mix(in srgb, var(--surface-sunk) 60%, transparent);
}

.cm-editor.cm-editor .cm-content { caret-color: var(--text); }

.cm-editor.cm-editor .cm-cursor,
.cm-editor.cm-editor .cm-dropCursor {
    border-left-color: var(--text);
}

/* CodeMirror's focused-selection rule is 0,5,0, which no reasonable selector
   beats, so this keeps the !important the old rule had. */
.cm-editor .cm-selectionBackground,
.cm-editor.cm-focused .cm-selectionBackground,
.cm-editor ::selection {
    background: color-mix(in srgb, var(--info) 30%, transparent) !important;
}

/* Cooklang syntax highlighting. editor.js assigns one fixed class per token
   kind (HighlightStyle `class`), so every colour resolves through the theme
   tokens and flips with light/dark like the rest of the editor chrome. */
.cm-editor .cm-cook-ingredient {
    color: var(--accent-text);
    font-weight: 600;
}

.cm-editor .cm-cook-cookware {
    color: var(--ok);
    font-weight: 600;
}

.cm-editor .cm-cook-timer {
    color: var(--danger);
    font-weight: 600;
}

.cm-editor .cm-cook-unit,
.cm-editor .cm-cook-metadata {
    color: var(--text-muted);
}

.cm-editor .cm-cook-comment {
    color: var(--text-faint);
    font-style: italic;
}

.cm-editor .cm-cook-section {
    color: var(--text);
    font-weight: 700;
    font-size: 1.1em;
}

.cm-editor .cm-cook-prep {
    color: var(--info);
    font-style: italic;
}

/* Lint (error/warning) hover bubble + autocomplete popup. Same reasoning as the
   block above: unlayered + doubled class to beat CodeMirror's runtime styles.
   Without this the popup keeps CodeMirror's hardcoded light background while the
   text inherits --text, so in dark mode it is near-white on near-white. */
.cm-tooltip.cm-tooltip {
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-control);
    box-shadow: var(--shadow-overlay);
}

.cm-tooltip.cm-tooltip .cm-tooltip-lint,
.cm-tooltip.cm-tooltip .cm-diagnostic,
.cm-tooltip.cm-tooltip .cm-diagnosticText {
    color: var(--text);
    background: transparent;
}

/* Accent bars — map CodeMirror's hardcoded #d11/orange/#999/#66d onto tokens so
   they read in both themes. */
.cm-tooltip.cm-tooltip .cm-diagnostic-error   { border-left-color: var(--danger); }
.cm-tooltip.cm-tooltip .cm-diagnostic-warning { border-left-color: var(--accent); }
.cm-tooltip.cm-tooltip .cm-diagnostic-info    { border-left-color: var(--info); }
.cm-tooltip.cm-tooltip .cm-diagnostic-hint    { border-left-color: var(--info); }