docs(adr): CSS Architecture
@aRustyDev ci sta già lavorando.
Dal 5/1/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
CSS Architecture
This document describes the CSS architecture for mdbook-htmx, including the modular file structure, CSS custom properties (variables) system, and theming approach.
Overview
mdbook-htmx uses a modular CSS architecture with:
- CSS Custom Properties for theming
- Modular file organization for maintainability
- HTMX-specific styles for transitions and indicators
- PostCSS for build-time processing
File Structure
book/assets/css/
├── base/
│ ├── reset.css # CSS reset/normalize
│ ├── variables.css # CSS custom properties
│ └── typography.css # Typography rules
├── layout/
│ ├── grid.css # Grid system
│ ├── sidebar.css # Sidebar layout
│ └── content.css # Content area
├── components/
│ ├── buttons.css # Button styles
│ ├── cards.css # Card components
│ ├── alerts.css # Alert/notification styles
│ └── search.css # Search input/results
├── htmx/
│ ├── indicators.css # Loading indicators
│ └── transitions.css # Swap transitions
├── themes/
│ ├── light.css # Light theme variables
│ └── dark.css # Dark theme variables
├── docs.css # Main entry point (imports all)
└── postcss.config.js # PostCSS configuration
CSS Custom Properties
Core Variables (base/variables.css)
:root {
/* Colors - Semantic */
--color-text: var(--color-gray-900);
--color-text-muted: var(--color-gray-600);
--color-text-inverse: var(--color-white);
--color-background: var(--color-white);
--color-background-alt: var(--color-gray-50);
--color-border: var(--color-gray-200);
--color-primary: var(--color-blue-600);
--color-primary-hover: var(--color-blue-700);
--color-success: var(--color-green-600);
--color-warning: var(--color-yellow-600);
--color-error: var(--color-red-600);
/* Colors - Palette */
--color-white: #ffffff;
--color-black: #000000;
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-200: #e5e7eb;
--color-gray-300: #d1d5db;
--color-gray-400: #9ca3af;
--color-gray-500: #6b7280;
--color-gray-600: #4b5563;
--color-gray-700: #374151;
--color-gray-800: #1f2937;
--color-gray-900: #111827;
--color-blue-500: #3b82f6;
--color-blue-600: #2563eb;
--color-blue-700: #1d4ed8;
--color-green-600: #16a34a;
--color-yellow-600: #ca8a04;
--color-red-600: #dc2626;
/* Typography */
--font-family-sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--font-family-mono: "JetBrains Mono", "Fira Code", Consolas, monospace;
--font-size-xs: 0.75rem;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
--font-size-2xl: 1.5rem;
--font-size-3xl: 1.875rem;
--font-size-4xl: 2.25rem;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--line-height-tight: 1.25;
--line-height-normal: 1.5;
--line-height-relaxed: 1.75;
/* Spacing */
--spacing-0: 0;
--spacing-1: 0.25rem;
--spacing-2: 0.5rem;
--spacing-3: 0.75rem;
--spacing-4: 1rem;
--spacing-5: 1.25rem;
--spacing-6: 1.5rem;
--spacing-8: 2rem;
--spacing-10: 2.5rem;
--spacing-12: 3rem;
--spacing-16: 4rem;
/* Layout */
--sidebar-width: 280px;
--sidebar-width-collapsed: 60px;
--content-max-width: 800px;
--header-height: 60px;
--border-radius-sm: 0.25rem;
--border-radius-md: 0.375rem;
--border-radius-lg: 0.5rem;
--border-radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
/* Transitions */
--transition-fast: 150ms ease;
--transition-normal: 200ms ease;
--transition-slow: 300ms ease;
/* Z-index scale */
--z-dropdown: 100;
--z-sticky: 200;
--z-modal: 300;
--z-tooltip: 400;
}
Theme System
Light Theme (themes/light.css)
:root,
[data-theme="light"] {
--color-text: var(--color-gray-900);
--color-text-muted: var(--color-gray-600);
--color-background: var(--color-white);
--color-background-alt: var(--color-gray-50);
--color-border: var(--color-gray-200);
--color-sidebar-bg: var(--color-gray-50);
--color-code-bg: var(--color-gray-100);
}
Dark Theme (themes/dark.css)
[data-theme="dark"] {
--color-text: var(--color-gray-100);
--color-text-muted: var(--color-gray-400);
--color-background: var(--color-gray-900);
--color-background-alt: var(--color-gray-800);
--color-border: var(--color-gray-700);
--color-sidebar-bg: var(--color-gray-800);
--color-code-bg: var(--color-gray-800);
}
Theme Switching
<html data-theme="light">
<body>
<button onclick="toggleTheme()">Toggle Theme</button>
</body>
</html>
<script>
function toggleTheme() {
const html = document.documentElement;
const current = html.getAttribute("data-theme");
html.setAttribute("data-theme", current === "dark" ? "light" : "dark");
localStorage.setItem("theme", html.getAttribute("data-theme"));
}
// Respect system preference
if (!localStorage.getItem("theme")) {
const prefersDark = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
document.documentElement.setAttribute(
"data-theme",
prefersDark ? "dark" : "light"
);
}
</script>
HTMX-Specific Styles
Loading Indicators (htmx/indicators.css)
/* Global loading indicator */
.htmx-indicator {
display: none;
opacity: 0;
transition: opacity var(--transition-normal);
}
.htmx-request .htmx-indicator {
display: inline-block;
opacity: 1;
}
/* Spinner */
.htmx-indicator.spinner {
width: 1rem;
height: 1rem;
border: 2px solid var(--color-border);
border-top-color: var(--color-primary);
border-radius: var(--border-radius-full);
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Progress bar */
.htmx-indicator.progress-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(
90deg,
var(--color-primary) 0%,
var(--color-primary) 30%,
transparent 30%
);
animation: progress 1s ease-in-out infinite;
}
@keyframes progress {
0% {
background-position: -100% 0;
}
100% {
background-position: 200% 0;
}
}
/* Skeleton loading */
.skeleton {
background: linear-gradient(
90deg,
var(--color-background-alt) 25%,
var(--color-border) 50%,
var(--color-background-alt) 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
Swap Transitions (htmx/transitions.css)
/* Fade transition */
.htmx-swapping {
opacity: 0;
transition: opacity var(--transition-normal);
}
.htmx-settling {
opacity: 1;
transition: opacity var(--transition-normal);
}
/* Slide transition */
.htmx-swapping.slide-out {
transform: translateX(-10px);
opacity: 0;
transition: all var(--transition-normal);
}
.htmx-settling.slide-in {
transform: translateX(0);
opacity: 1;
transition: all var(--transition-normal);
}
/* Content swap animation */
#content.htmx-swapping > * {
opacity: 0.5;
transition: opacity var(--transition-fast);
}
#content.htmx-settling > * {
opacity: 1;
transition: opacity var(--transition-normal);
}
/* Disable transitions for reduced motion preference */
@media (prefers-reduced-motion: reduce) {
.htmx-swapping,
.htmx-settling,
.htmx-indicator {
transition: none;
animation: none;
}
}
Component Styles
Buttons (components/buttons.css)
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--spacing-2);
padding: var(--spacing-2) var(--spacing-4);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
border-radius: var(--border-radius-md);
border: 1px solid transparent;
cursor: pointer;
transition: all var(--transition-fast);
}
.btn-primary {
background: var(--color-primary);
color: var(--color-text-inverse);
}
.btn-primary:hover {
background: var(--color-primary-hover);
}
.btn-secondary {
background: transparent;
color: var(--color-text);
border-color: var(--color-border);
}
.btn-secondary:hover {
background: var(--color-background-alt);
}
/* Loading state */
.btn.htmx-request {
pointer-events: none;
opacity: 0.7;
}
Alerts (components/alerts.css)
.alert {
padding: var(--spacing-4);
border-radius: var(--border-radius-md);
border: 1px solid;
}
.alert-info {
background: var(--color-blue-50, #eff6ff);
border-color: var(--color-blue-200, #bfdbfe);
color: var(--color-blue-800, #1e40af);
}
.alert-success {
background: var(--color-green-50, #f0fdf4);
border-color: var(--color-green-200, #bbf7d0);
color: var(--color-green-800, #166534);
}
.alert-warning {
background: var(--color-yellow-50, #fefce8);
border-color: var(--color-yellow-200, #fef08a);
color: var(--color-yellow-800, #854d0e);
}
.alert-error {
background: var(--color-red-50, #fef2f2);
border-color: var(--color-red-200, #fecaca);
color: var(--color-red-800, #991b1b);
}
/* Access denied variant */
.alert-access-denied {
text-align: center;
padding: var(--spacing-8);
}
.alert-access-denied .icon {
font-size: var(--font-size-4xl);
margin-bottom: var(--spacing-4);
}
Search (components/search.css)
.search-container {
position: relative;
}
.search-input {
width: 100%;
padding: var(--spacing-2) var(--spacing-4);
padding-left: var(--spacing-10);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-md);
background: var(--color-background);
color: var(--color-text);
font-size: var(--font-size-sm);
}
.search-input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgb(37 99 235 / 0.1);
}
.search-icon {
position: absolute;
left: var(--spacing-3);
top: 50%;
transform: translateY(-50%);
color: var(--color-text-muted);
}
.search-results {
position: absolute;
top: 100%;
left: 0;
right: 0;
margin-top: var(--spacing-2);
background: var(--color-background);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-md);
box-shadow: var(--shadow-lg);
max-height: 400px;
overflow-y: auto;
z-index: var(--z-dropdown);
}
.search-result-item {
padding: var(--spacing-3) var(--spacing-4);
border-bottom: 1px solid var(--color-border);
}
.search-result-item:last-child {
border-bottom: none;
}
.search-result-item:hover {
background: var(--color-background-alt);
}
.search-result-title {
font-weight: var(--font-weight-medium);
color: var(--color-text);
}
.search-result-excerpt {
font-size: var(--font-size-sm);
color: var(--color-text-muted);
margin-top: var(--spacing-1);
}
.search-result-highlight {
background: var(--color-yellow-200, #fef08a);
padding: 0 2px;
border-radius: 2px;
}
Layout Styles
Grid System (layout/grid.css)
.container {
display: grid;
grid-template-columns: var(--sidebar-width) 1fr;
min-height: 100vh;
}
@media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
}
}
Sidebar (layout/sidebar.css)
.sidebar {
position: sticky;
top: var(--header-height);
height: calc(100vh - var(--header-height));
overflow-y: auto;
padding: var(--spacing-4);
background: var(--color-sidebar-bg);
border-right: 1px solid var(--color-border);
}
.sidebar-nav a {
display: block;
padding: var(--spacing-2) var(--spacing-3);
color: var(--color-text);
text-decoration: none;
border-radius: var(--border-radius-md);
}
.sidebar-nav a:hover {
background: var(--color-background-alt);
}
.sidebar-nav a.active {
background: var(--color-primary);
color: var(--color-text-inverse);
}
/* Collapsible sections */
.sidebar-section {
margin-bottom: var(--spacing-2);
}
.sidebar-section-toggle {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: var(--spacing-2);
font-weight: var(--font-weight-medium);
cursor: pointer;
}
.sidebar-section-toggle .chevron {
transition: transform var(--transition-fast);
}
.sidebar-section.collapsed .chevron {
transform: rotate(-90deg);
}
.sidebar-section.collapsed .sidebar-section-content {
display: none;
}
PostCSS Configuration
// postcss.config.js
module.exports = {
plugins: [
require("postcss-import"),
require("postcss-nesting"),
require("autoprefixer"),
process.env.NODE_ENV === "production" && require("cssnano"),
].filter(Boolean),
};
Main Entry Point
/* docs.css */
@import "base/reset.css";
@import "base/variables.css";
@import "base/typography.css";
@import "themes/light.css";
@import "themes/dark.css";
@import "layout/grid.css";
@import "layout/sidebar.css";
@import "layout/content.css";
@import "components/buttons.css";
@import "components/cards.css";
@import "components/alerts.css";
@import "components/search.css";
@import "htmx/indicators.css";
@import "htmx/transitions.css";
Related Documentation
- Lingua principale
- Rust
- Stelle
- 0
- Fork
- 1
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di aRustyDev/mdbook-htmx
-
feat(ci): Implement cross-repo-validate compliance for docs syncForse di nuovo libera @aRustyDev l’ha presa 280 giorni fa e non c’è nessuna pull request aperta. Apertaci enhancement
aRustyDev/mdbook-htmx#58 · 1 commento · 1 assegnatario ·
-
docs(htmx): Explain how HTMX worksForse di nuovo libera @aRustyDev l’ha presa 283 giorni fa e non c’è nessuna pull request aperta. Aperta
aRustyDev/mdbook-htmx#48 · 3 commenti · 1 assegnatario ·
-
docs(mdbook-htmx): Parent Issue for documentationForse di nuovo libera @aRustyDev l’ha presa 283 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation
aRustyDev/mdbook-htmx#47 · 1 assegnatario ·
-
docs(examples): Secure Self-Hosted Meilisearch via CloudflareForse di nuovo libera @aRustyDev l’ha presa 283 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation
aRustyDev/mdbook-htmx#46 · 1 assegnatario ·
-
docs(examples): Kubernetes with MeilisearchForse di nuovo libera @aRustyDev l’ha presa 283 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation
aRustyDev/mdbook-htmx#45 · 1 assegnatario ·
Tutte le issue di aRustyDev/mdbook-htmx
Issue simili
-
area/infra good first issue type/bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 60/100
cristim/AztecCraft#92 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
charles-hood/DiagramCraft#51 ·
-
S: Untriaged T: Bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
libnyanpasu/clash-nyanpasu#5788 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
area:cli bug good first issue priority:medium
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
rtk-ai/rtk#4589 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno