Skip to main content

Zenzic Style Guide

"The rigour applied to code must extend to every pixel the user sees."

This document codifies the Zenzic Visual Language — the binding rules for all Zenzic documentation pages. Every contributor must follow these rules. Reviewers must reject PRs that violate them.

Directive: ZRT-DOC-002


1. Card Rule (High-Density UX)

Navigation cards orient. They do not replace the sidebar.

Structure

Every card in a <div class="grid cards" markdown> block must have exactly:

  1. An icon (<Icon name="..." /> — see §3).
  2. A bold title.
  3. A description of at most two lines.
  4. A single action link using the arrow prefix.

Canonical example


- <Icon name="play" /> &nbsp; **User Guide**

Everything you need to install, configure, and integrate Zenzic into
your CI/CD workflow.

[<Icon name="arrow-right" /> Explore the Guide](../../../how-to/install.mdx)

Forbidden patterns

PatternWhy
Horizontal link chains (·-separated)Creates a wall of text; impossible to scan
Nested <li> lists inside a cardBreaks card height uniformity
--- separators inside a cardAdds visual noise without information gain
Cards with zero action linksDead-end; the user has nowhere to go

Exception

Presentation cards (e.g., homepage "Zenzic in Action" demos) may omit the action link because their purpose is visual demonstration, not navigation. They must still receive the card CSS (border, hover, transition).


2. Admonition Taxonomy

Each admonition type has one — and only one — semantic role.

TypeRoleWhen to use
:::tipQuick WinOne-liner commands the reader can run immediately
:::infoZenzic OutputCLI output blocks and Zenzic report samples
:::dangerSecurity GateExit Code 2 (credentials) and Exit Code 3 (path traversal) only
:::warningDesign ConstraintArchitectural rules, contributor policies, "use sparingly" caveats
:::noteClarificationEngine-specific facts, contributor onboarding, multi-step guidance
:::infoCross-Reference BridgeLinks from the current section to the next actionable step
:::infoCommunity CTAEngagement calls ("Help us grow", "Join the discussion")
:::notePhilosophyProject vision, design manifesto, Zenzic standards

Enforcement

If a block does not fit any category above, rewrite it as prose. Admonitions are not decoration.


3. Iconography Law (ZRT-DOC-003)

The <Icon /> Component

Every icon in the documentation must be rendered with:

<Icon name="icon-name" />

Optional size override (default is 1.15em, inherits from surrounding text):

<Icon name="shield-check" size={20} />

All icon names follow the Lucide icon set naming convention (lowercase, hyphen-separated).

Hierarchy

PrioritySetSyntaxNotes
1Lucide<Icon name="play" />All UI and navigation icons

Rules

  • Semantic consistency: if an icon represents "Contribute" on one page, it

    must be the same icon on every page.

  • Uniform syntax: every icon in a card grid uses <Icon name="..." />.

    No mixing of syntaxes or icon sets.

  • Tree-shaking contract: before using a new icon name, add it to the

    explicit iconsMap in src/components/Icon.tsx. Unregistered names render a red placeholder and emit a console.warn.


4. Anchor ID Protocol (ZRT-DOC-004)

When to add explicit IDs

Add {#id} to a heading when it satisfies both of:

  1. It is an H2 or H3 heading (never H1 — Docusaurus auto-generates H1 IDs from sidebar_label).
  2. It is referenced by a cross-page link ([text](page.mdx#anchor)).

i18n Invariant

The canonical ID is always the English slug. Italian (and any future language) pages must use the same {#id} value:

{/* EN */}
## Getting Started \{#getting-started}

{/* IT */}
## Inizia Ora \{#getting-started}

This ensures the VSM resolver and cross-language links never break due to translation-dependent slug generation.

Heading format

## Section Title \{#section-title}

Do not add IDs to headings that are never linked to externally. Every explicit ID is a maintenance contract.


5. Code Block Rule

Every opening fence must carry a language tag:

FenceVerdict
```python
```bash
```toml
```text✓ (plain output)
```FORBIDDEN

Use text for output that has no syntax highlighting. Naked fences hurt accessibility tools and syntax highlighters.

Gutter specificity: for CLI output shown inside :::info blocks, always use the text tag to prevent the syntax highlighter from generating random colours on log strings or file paths.


6. SPDX Header

Every .md file must begin with:

{/* SPDX-FileCopyrightText: 2026 PythonWoods <[email protected]> */}
{/* SPDX-License-Identifier: Apache-2.0 */}

Files with YAML frontmatter place the SPDX block immediately after the closing ---.


7. Visual Consistency Checklist

Before submitting a PR, verify:

  • Every card grid follows §1 (single action link).
  • Every admonition matches its §2 role.
  • All icons use <Icon name="..." /> — no :lucide-*:, :octicons-*:, or :material-*: shortcodes remain (§3).
  • Any new icon name is registered in src/components/Icon.tsx (§3).
  • Cross-referenced H2/H3 headings have explicit {#id} (§4). No anchors on H1.
  • No naked code fences exist (§5).
  • SPDX header is present (§6).
  • Italian mirror is structurally identical to English.
  • No hex literal (#rrggbb) in src/ outside ZenzicPalette._* (§9).
  • All colour references use ZenzicPalette.* — no removed flat constants (§9).
  • No new .svg file added to static/assets/terminal/ (§10).
  • Any text-bearing SVG inside an MDX page is implemented as .tsx (§10).

8. ZenzicUI Gateway

All branded terminal output in Zenzic flows through a single object: ZenzicUI in src/zenzic/ui.py. Command modules must never instantiate Console or ZenzicUI directly — they must call get_ui() and get_console() from zenzic.cli._shared.

Core methods

MethodWhen to use
print_header(version)The top-of-output Zenzic Frame banner — once per command invocation
make_panel(content, *, title, border_style)Styled Rich Panel — for structured output blocks
print_exception_alert(message, *, context, title, border_style)Error panels for ZenzicError and PluginContractError

Usage pattern

# In any _check.py / _clean.py / _standalone.py command
from . import _shared

# Print the Zenzic banner header
_shared.get_ui().print_header(__version__)

# Print a styled panel
panel = _shared.get_ui().make_panel(
"Content here",
title="Panel Title",
border_style="bold cyan",
)
_shared.get_console().print(panel)

Why the gateway matters

The --no-color and --force-color CLI flags call configure_console(), which atomically replaces the module-level console and _ui singletons. Any locally-created Console or ZenzicUI instance will be frozen before the flag takes effect, silently ignoring the user's color preference.

The force_terminal parameter must always be None (auto-detect) in the module-level Console, never False. Explicit False disables color system detection entirely — resulting in no ANSI styling even in truecolor terminals. This is the most common source of visual regressions in the Zenzic CLI layer.

Checklist addition

Add to your PR checklist:

  • No Console(...) or ZenzicUI(...) instantiation in command modules.
  • All banner output uses get_ui().print_header(), not a locally-created UI instance.
  • force_terminal on any new Console call is None or conditional (True if ... else None), never False.

9. ZenzicPalette — Zero Hex Law

ZenzicPalette in src/zenzic/ui.py is the sole authorised source of colour values in the entire Zenzic codebase. This is the Zero Hex Law.

The Law

:::warning Design Constraint

No hex colour string (e.g. #4f46e5) and no raw Rich colour name (e.g. "red", "cyan") may appear anywhere in src/ except inside ZenzicPalette._* private class attributes. Every other file must address only the semantic public attributes shown below.

:::

Semantic palette

AttributeHexMeaning
ZenzicPalette.BRAND#4f46e5Zenzic primary / brand accent (Indigo)
ZenzicPalette.SUCCESS#10b981OK · clean · pass (Emerald)
ZenzicPalette.WARNING#f59e0bCaution · advisory (Amber)
ZenzicPalette.ERROR#f43f5eFailure · broken links (Rose)
ZenzicPalette.DIM#64748bMuted · secondary text (Slate)
ZenzicPalette.FATAL#8b0000Security breach · path traversal (Critical Red)

Pre-composed style strings

For the most common combinations, use a STYLE_* constant instead of constructing f"bold {X}" inline:

ConstantExpands to
ZenzicPalette.STYLE_BRAND"bold #4f46e5"
ZenzicPalette.STYLE_OK"bold #10b981"
ZenzicPalette.STYLE_WARN"bold #f59e0b"
ZenzicPalette.STYLE_ERR"bold #f43f5e"
ZenzicPalette.STYLE_DIM"#64748b"

Usage pattern

# CORRECT — semantic alias via ZenzicPalette
from zenzic.ui import ZenzicPalette

table = Table(border_style=ZenzicPalette.DIM, header_style=ZenzicPalette.STYLE_BRAND)
text = Text.from_markup(f"[{ZenzicPalette.BRAND}]Zenzic[/]")
panel = Panel("...", border_style=ZenzicPalette.STYLE_ERR)
# FORBIDDEN — hex literal outside ZenzicPalette
text = Text.from_markup("[#4f46e5]Zenzic[/]") # ✗

# FORBIDDEN — flat constant import (removed in)
from zenzic.ui import INDIGO, EMERALD # ✗

# FORBIDDEN — inline alias
P = ZenzicPalette # ✗ use full qualification

Updating the palette

To change a colour, edit only the corresponding _PRIVATE hex attribute inside ZenzicPalette in src/zenzic/ui.py. All semantic aliases and pre-composed style strings derive from those private attributes — the entire codebase updates automatically.

Checklist addition

Add to your PR checklist:

  • No hex literal (#rrggbb) anywhere in src/ outside ZenzicPalette._*.
  • No raw Rich colour names ("red", "cyan") for brand-palette usage — use ZenzicPalette.*.
  • No local alias P = ZenzicPalette — always use the full class name.
  • No from zenzic.ui import INDIGO (or any removed flat constant).

10. MDX Asset Componentization Law

Directive: ZRT-DOC-010

:::warning Design Constraint

Any vector asset intended for exclusive use within MDX pages must be implemented as a React component (.tsx), never as a static .svg file.

:::

Rationale

Static .svg files cannot:

  • React to Docusaurus theme variables (--ifm-color-*, light/dark mode switching).
  • Use <Translate> for i18n — text is baked into XML, requiring manual duplication.
  • Update deterministically when the underlying data model changes — they require find-and-replace surgery across XML nodes.

React components (.tsx) solve all three: they read CSS variables at runtime, wrap strings with <Translate>, and derive their data from a single source of truth.

Permitted uses of static .svg files

Use casePermitted?Reason
OpenGraph social cards (static/assets/social/)Consumed by <meta og:image>, not by React
GitHub README illustrations (wordmarks, logos)Rendered by GitHub Markdown, no React context
Architecture/brand diagrams (static/assets/brand/)Pure graphics — no data nodes, no text strings
Terminal output panels inside MDX pagesUse <ZenzicOutput /> instead
Any text-bearing illustration inside an MDX pageUse a .tsx component

Migration path

When a static SVG carrying text or data nodes must be converted:

  1. Create src/components/<Name>.tsx with the same visual layout.
  2. Replace text literals with <Translate id="..."> strings.
  3. Source numeric data (weights, codes, scores) from the canonical Python module via a generated JSON fixture, not from hardcoded SVG attributes.
  4. Register the component in src/theme/MDXComponents.js for global MDX availability.
  5. Delete the original .svg file.

Checklist addition

Add to your PR checklist:

  • No new .svg file added to static/assets/terminal/ (use ZenzicOutput or a .tsx component).
  • Any text-bearing SVG introduced inside an MDX page is implemented as .tsx.