Properties4
3Lithos's design system embodies a philosophy we call "Native Clarity"—a visual language that feels native to the platform (macOS and modern web standards) while maintaining crystal-clear information hierarchy and readability. Every color, spacing decision, and typographic choice serves the goal of effortless knowledge consumption. This document details the technical implementation of that vision, from color tokens to component theming.
Native Clarity Design Philosophy
The design philosophy guides every visual decision in Lithos, ensuring consistency and intentionality across the entire interface.
Platform-Aligned Aesthetics
Native Clarity means your documentation should feel like a natural extension of the operating system. On macOS, that means respecting system conventions: subtle shadows, translucent backgrounds, and familiar interaction patterns. On the web, it means following modern standards: semantic HTML, accessible color contrast, and progressive enhancement.
The design avoids what we call "framework face"—the generic, instantly recognizable aesthetic of out-of-the-box UI frameworks. While Lithos is built on Nuxt UI and Docus, it extensively customizes the defaults to create a distinct visual identity. The goal is that readers can't immediately identify which framework powers the site; they just perceive a polished, cohesive interface.
This platform alignment extends to typography. Rather than using ubiquitous web fonts like Roboto or Inter, Lithos uses Mona Sans—a modern, geometric sans-serif that feels fresh without being trendy. For monospace, Monaspace Neon provides exceptional readability for code, with its ligatures and character spacing optimized for programming languages. These choices signal attention to detail and respect for craft.
Information Hierarchy Without Noise
Clarity means every element has a clear purpose and visual weight proportional to its importance. Titles are large and bold. Body text is generous in size and line height. Metadata (dates, tags, folders) is small and muted. This hierarchy is enforced through systematic use of CSS variables for font sizes, weights, and colors, ensuring consistency across all pages and components.
The design avoids decorative elements that don't serve information architecture. There are no divider lines that don't separate distinct sections. No background textures that compete with text. No gratuitous animations that distract from reading. Every visual element either conveys information or improves usability—anything else is removed.
This ruthless focus on content also means generous whitespace. The layout breathes, giving readers' eyes room to rest and parse information. The Interactive Graph has ample margins. Code blocks have comfortable padding. List items are spaced to prevent visual crowding. This spaciousness is a deliberate choice, prioritizing reading comfort over information density.
Dark Mode as a First-Class Citizen
Many sites treat dark mode as an afterthought, inverting colors and hoping for the best. Lithos designs light and dark modes in parallel, ensuring both feel intentional and polished. Dark mode isn't just "make the background black"; it's a carefully calibrated palette with adjusted contrast ratios, dimmed whites (to avoid eye strain), and accent colors that pop without glaring.
The graph visualization, in particular, is designed with dark mode in mind. Node colors shift in hue and saturation to remain visible against the dark background without becoming neon. Edge opacity is carefully tuned to balance visibility with clutter. Text labels use near-white instead of pure white, reducing harshness during long reading sessions.
Color System: Violet and Zinc
The color palette is intentionally minimal, anchored by a custom violet for accents and a zinc neutral scale for typography and backgrounds.
Violet Primary (#A58AF9)
The primary accent color is a soft, luminous violet (#A58AF9, officially --color-violet-400). This hue was chosen for several reasons. First, it's distinct—violet is rare in documentation sites, most of which default to blue. Second, it's versatile, working equally well for links, buttons, and highlights without becoming overwhelming. Third, it has a technical, almost digital quality that suits a developer tool while remaining approachable.
The violet scale spans from near-white (#f5f3ff, violet-50) to deep purple (#2e1065, violet-950), providing 11 stops for various UI needs. Lighter stops are used for hover states and subtle backgrounds. Mid-range stops are for primary actions and focus indicators. Darker stops are for pressed states and high-contrast elements. This scale is defined via the @theme directive in main.css, making it available as Tailwind utilities (bg-violet-400, text-violet-600, etc.).
The violet primary also appears in the Interactive Graph, where it colors default nodes. This creates visual continuity between the graph and the rest of the interface, reinforcing that the graph is an integral part of the design system, not a third-party widget grafted on.
Zinc Neutrals for macOS Grays
For grays, Lithos uses Tailwind's zinc scale instead of gray or slate. Zinc has a subtle warmth that pairs well with violet and closely matches the neutral grays in macOS Big Sur and later. This makes the documentation feel native to the Mac ecosystem, which is Obsidian's primary platform.
Zinc is used for body text (--ui-text), borders (--ui-border), and muted backgrounds (--ui-bg-muted). The specific stops are calibrated for WCAG AA contrast ratios in both light and dark modes. For example, zinc-900 is used for text on light backgrounds, while zinc-100 is used for text on dark backgrounds, ensuring readability in all contexts.
The neutral scale also includes semantic tokens like --ui-text-muted and --ui-text-dimmed, which map to specific zinc stops. These semantic tokens make it easy to adjust the overall tone of the interface by changing a few variables, rather than hunting through component styles.
Graph-Specific Color Tokens
The graph visualization uses a specialized set of color tokens prefixed with --lithos-graph-*. These tokens define colors for different node types, edges, and interaction states. For example, --lithos-graph-node-default is the base node color (the violet primary), while --lithos-graph-node-current is a green (#30d158, borrowed from macOS system accents) used to highlight the current page in the graph.
Folder-based node coloring is achieved with tokens like --lithos-graph-folder-ai (blue) and --lithos-graph-folder-blog (red). These colors are assigned based on the folder structure of your vault, creating visual clusters in the graph that correspond to content categories. You can customize these colors by overriding the CSS variables in your own stylesheet, allowing for brand-specific graph palettes.
The edge color (--lithos-graph-edge) is a semi-transparent gray (rgba(136, 136, 136, 0.4)), ensuring edges remain visible but don't overpower nodes. On hover, edges become more opaque and shift to the violet accent color, providing clear visual feedback for interaction.
CSS Variables and @theme Directive
Lithos uses Tailwind 4's @theme directive to define custom CSS variables in a way that integrates seamlessly with Tailwind's utility class system.
@theme Directive in main.css
The @theme static block in assets/css/main.css defines core design tokens like fonts and the violet color scale. This directive tells Tailwind to inject these tokens into its configuration, making them available as utility classes and ensuring they're included in the purge process.
@theme static {
--font-sans: 'Mona Sans', system-ui, sans-serif;
--font-mono: 'Monaspace Neon', ui-monospace, monospace;
--color-violet-50: #f5f3ff;
--color-violet-400: #A58AF9;
--color-violet-950: #2e1065;
}
The static keyword indicates these variables are constant and don't change based on context (unlike dynamic variables that might shift based on component state). This allows Tailwind to optimize the generated CSS, eliminating unused variants.
Using @theme rather than raw CSS variables in :root ensures proper integration with Tailwind's design token system. For example, defining --color-violet-400 via @theme allows you to use text-violet-400 in your templates, and Tailwind will resolve it correctly. If you used a plain CSS variable, Tailwind wouldn't recognize it, and you'd have to write text-[color:var(--color-violet-400)] instead.
CSS Variables for Runtime
Some tokens need to change at runtime based on user preferences (like dark mode) or component state. These are defined as standard CSS variables in the :root selector, outside the @theme block.
:root {
--lithos-graph-bg: transparent;
--lithos-graph-node-default: #a78bfa;
--lithos-graph-text: #18181b;
}
.dark {
--lithos-graph-text: #e5e5e5;
}
This pattern allows for theme-aware styling. The --lithos-graph-text variable changes from dark gray (zinc-900) in light mode to light gray (zinc-100) in dark mode. Components reference var(--lithos-graph-text) in their styles, automatically adapting to the current theme without conditional logic.
These runtime variables are also useful for component-specific theming. A component can override a variable within its scope, creating a local theme. For example, a callout component might set --lithos-accent-primary: red for "danger" callouts, overriding the global violet accent within that component tree.
Semantic Tokens for Abstraction
Lithos defines semantic tokens like --lithos-text-primary and --lithos-border-subtle that map to Nuxt UI's internal tokens (--ui-text-highlighted, --ui-border-muted). This abstraction layer allows Lithos to adapt to Nuxt UI updates without rewriting component styles.
If Nuxt UI changes its token naming in a future version, Lithos can update the mapping in one place (main.css), and all components continue to work. This architectural decision prioritizes maintainability and future-proofing, reducing technical debt as dependencies evolve.
Typography: Mona Sans and Monaspace Neon
Typography is the foundation of readable documentation, and Lithos invests in high-quality fonts that enhance the reading experience.
Mona Sans for Body and UI
Mona Sans is a variable font developed by GitHub and Degarism Studio, designed for both UI and text use. It's a neo-grotesque sans-serif with a clean, modern aesthetic that feels professional without being sterile. The variable font format allows Lithos to use a single font file for all weights (from 200 to 900), reducing bandwidth and ensuring consistent rendering.
The font is hosted via Nuxt Fonts, which automatically generates font-face declarations and optimizes loading. The configuration in nuxt.config.ts ensures the font is preloaded, preventing FOIT (Flash of Invisible Text) during page load. The font files are also subset to include only the characters used on the site, further reducing load times.
Mona Sans's wide apertures and tall x-height make it exceptionally readable at small sizes, which is crucial for metadata, captions, and sidebar text. At larger sizes (titles and headings), its geometric structure creates strong visual hierarchy without needing heavy weights. This versatility means Lithos can use a single font family for the entire interface, reducing complexity.
Monaspace Neon for Code
Code deserves special typographic treatment. Monaspace Neon, part of GitHub's Monaspace font family, is optimized for programming with features like coding ligatures, wide spacing for visual distinction, and a futuristic aesthetic that matches Lithos's technical focus.
The font includes ligatures for common programming symbols (like =>, !=, and ...), improving scannability of code blocks. These ligatures are enabled by default in main.css via font-feature-settings: "liga" 1, "calt" 1, ensuring they work across browsers.
Monaspace Neon's character design emphasizes differentiation. Similar characters (like 1, l, and I, or 0 and O) are easily distinguished, reducing errors when reading code. The font also has a slightly wider tracking (letter spacing) than typical monospace fonts, which improves readability in long code samples without sacrificing alignment.
Font Loading and Performance
Fonts are loaded via @nuxt/fonts, which handles subsetting, preloading, and format optimization. The module generates a @font-face declaration with font-display: swap, ensuring text remains visible during font load. The fonts are also preconnected in the HTML <head>, reducing DNS lookup time.
In production, fonts are served from the same origin as the site (via the .nuxt/fonts directory), eliminating cross-origin requests and improving caching. The variable font format means only two font files are needed (one for each family), compared to the dozen or more files required for traditional font stacks with multiple weights and styles.
Component Theming via app.config.ts
Nuxt UI components are themed through the app.config.ts file, which allows for deep customization without overriding component source code.
UPage Layout Overrides
The UPage component provides the base layout for all documentation pages. Lithos customizes it to use a 10-column grid with specific column spans for the left sidebar (2 columns), center content (7 or 8 columns, depending on whether the right sidebar is shown), and right sidebar (3 columns).
page: {
slots: {
root: 'flex flex-col lg:grid lg:grid-cols-10 lg:gap-4',
left: 'lg:col-span-2',
center: 'lg:col-span-7',
right: 'lg:col-span-3'
}
}
This configuration is set in the ui key of app.config.ts, which Nuxt UI reads to override its default styles. The slots object maps slot names to Tailwind utility classes, providing fine-grained control over layout without CSS.
The compoundVariants array handles conditional styling. For example, when the right sidebar is hidden (no table of contents), the center content expands to 8 columns. This logic is declarative and type-checked, preventing errors from manual class manipulation.
UButton and UCommandPalette Styling
Buttons and command palette components receive custom styles to align with the Native Clarity philosophy. Buttons use the font-medium weight (500) instead of the default bold (700), creating a more refined, less shouty appearance. The command palette's match highlighting is styled to use the violet accent color with a semibold weight, ensuring highlighted text stands out without being garish.
button: {
slots: {
base: 'font-medium'
}
},
commandPalette: {
slots: {
itemLabelHighlight: 'text-primary-500 dark:text-primary-400 font-semibold'
}
}
These overrides are applied globally, ensuring all instances of these components use the custom styling. If you need component-specific styling, you can use the class prop on individual component instances, which merges with the global theme.
Content Navigation Variant
The content navigation (sidebar links) uses the link variant instead of the default pill variant. The pill variant highlights the entire row with a background color, which can feel heavy in dense navigation. The link variant highlights only the text and adds a subtle indicator line on the left, creating a lighter, more scannable sidebar.
contentNavigation: {
defaultVariants: {
variant: 'link',
},
}
This configuration also includes slot overrides for icon sizes (size-4) and hiding trailing icons (collapse indicators), as Lithos uses a flat navigation structure without expandable sections.
Dark Mode Support via Nuxt Color Mode
Dark mode is implemented using the @nuxtjs/color-mode module, which provides automatic theme detection, persistence, and a toggle component.
Automatic Theme Detection
On first visit, Nuxt Color Mode reads the user's system preference via the prefers-color-scheme media query. If the OS is set to dark mode, the site loads in dark mode; otherwise, it loads in light mode. This respects user preferences without requiring configuration.
The detected theme is stored in localStorage under the nuxt-color-mode key. On subsequent visits, the stored preference overrides system detection, allowing users to choose a theme that differs from their OS setting. This flexibility is important for users who prefer dark documentation even if their OS is in light mode (or vice versa).
.dark Class Strategy
Nuxt Color Mode applies a .dark class to the <html> element when dark mode is active. All dark mode styles are scoped under this class, ensuring they only apply in dark mode. This class-based approach is more reliable than media query-based dark mode, as it allows for user overrides independent of OS settings.
:root {
--lithos-graph-text: #18181b;
}
.dark {
--lithos-graph-text: #e5e5e5;
}
The .dark class strategy also enables JavaScript-based theme toggling. The theme toggle button in the header calls $colorMode.preference = 'dark', which adds the class and updates localStorage, all in a single operation.
Theme Toggle Component
The theme toggle is a simple button in the header that cycles between light, dark, and system modes. The icon changes to reflect the current mode (sun for light, moon for dark, auto for system). Clicking the toggle updates the preference and triggers a smooth transition between themes.
The transition is achieved with CSS transitions on color and background-color properties, applied globally with .color-mode-transition class. This prevents jarring flashes when switching themes, creating a polished, app-like experience.
Responsive Breakpoints
Lithos defines three breakpoints that govern layout shifts across device sizes: mobile, tablet, and desktop.
Mobile (<768px)
On mobile, the layout is fully stacked. The left sidebar (navigation) is hidden behind a hamburger menu. The right sidebar (table of contents) is hidden entirely or moved to the bottom of the page. The center content spans the full width, with generous horizontal padding (16px) to ensure text doesn't touch the screen edges.
The graph visualization adjusts to mobile by reducing node sizes, hiding labels on distant nodes, and disabling certain interactions (like dragging) that conflict with touch scrolling. The graph remains interactive but prioritizes readability over feature completeness.
Typography also scales down on mobile. Base font size reduces slightly (from 16px to 15px), and line height tightens to fit more content on screen. Headings scale proportionally, maintaining hierarchy without overwhelming the small viewport.
Tablet (768px-1023px)
Tablet layout is a hybrid: the left sidebar is visible (fixed at 2 columns), but the right sidebar is hidden. The center content spans 8 columns, giving it ample width for comfortable reading. This layout is ideal for iPad users who have enough screen real estate for a sidebar but benefit from a wider reading column than desktop.
The hamburger menu is hidden on tablet, as the navigation is always visible. This reduces UI clutter and makes the interface feel more "desktop-like," which tablet users often prefer. The table of contents can be accessed via a floating button or simply by scrolling through the content, which is often faster than clicking TOC links on tablet.
Graph interactions on tablet are optimized for touch. Node sizes increase slightly to provide larger tap targets, and hover states are replaced with tap-and-hold to reveal labels. The graph is fully pannable and zoomable via pinch gestures, leveraging native browser touch APIs.
Desktop (>=1024px)
Desktop layout uses the full three-column structure: navigation (2 columns), content (7 columns), and table of contents (3 columns). This maximizes information density while maintaining readability. The sidebar columns are fixed-width (via grid fractions), ensuring consistent layout across wide screens.
On ultra-wide monitors (>1920px), the layout is constrained to a maximum width (via max-w-screen-2xl or a custom token) and centered. This prevents lines of text from becoming too long, which hurts readability. The max-width is applied to the outer container, so sidebars remain visible at the edges even on very wide screens.
Desktop interactions are optimized for mouse and keyboard. Hover states are prominent, keyboard shortcuts are enabled (e.g., / to open search), and focus indicators are visible for accessibility. The graph supports precise mouse control, with dragging, zooming via scroll, and detailed tooltips on hover.
--color-violet-* variables in your own CSS. You can create a completely different brand palette (e.g., a teal theme) by redefining just 11 color stops.app.config.ts. Refer to these sources for the full catalog of available tokens and overrides.