771 lines
14 KiB
Markdown
771 lines
14 KiB
Markdown
---
|
|
version: "1.0"
|
|
date: "2025-01-15"
|
|
author: "DWS Foldsite Team"
|
|
title: "Styles Guide"
|
|
description: "Understanding Foldsite's CSS cascade system"
|
|
summary: "Learn how CSS styles cascade through your Foldsite project - from base styles to page-specific customizations."
|
|
quick_tips:
|
|
- "base.css is required and loaded on every page"
|
|
- "All matching styles are loaded (unlike templates where first match wins)"
|
|
- "Styles cascade down through directory structure like templates"
|
|
---
|
|
|
|
# Styles Guide
|
|
|
|
Foldsite's style system follows the same hierarchical logic as templates, allowing you to organize CSS in a maintainable, scalable way.
|
|
|
|
## The Style System
|
|
|
|
### Key Differences from Templates
|
|
|
|
| Templates | Styles |
|
|
|-----------|--------|
|
|
| **First match wins** | **All matches load** |
|
|
| One template per page | Multiple stylesheets per page |
|
|
| Must have at least one | base.css required |
|
|
|
|
**Why the difference?**
|
|
|
|
CSS is designed to cascade and layer. Loading multiple stylesheets allows you to:
|
|
- Share base styles
|
|
- Add section-specific styles
|
|
- Override with page-specific styles
|
|
|
|
## Required Style: base.css
|
|
|
|
Every Foldsite project **must have** `styles/base.css`.
|
|
|
|
This file is **loaded on every page**, providing:
|
|
- Typography
|
|
- Layout basics
|
|
- Color scheme
|
|
- Resets/normalizes
|
|
|
|
### Minimal base.css
|
|
|
|
```css
|
|
/* styles/base.css */
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
|
|
line-height: 1.6;
|
|
color: #333;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 2rem;
|
|
}
|
|
|
|
h1, h2, h3, h4, h5, h6 {
|
|
margin-top: 1.5em;
|
|
margin-bottom: 0.5em;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
a {
|
|
color: #0066cc;
|
|
text-decoration: none;
|
|
}
|
|
|
|
a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
}
|
|
```
|
|
|
|
## Style Discovery
|
|
|
|
Styles follow a **hierarchical discovery pattern** similar to templates.
|
|
|
|
### For File: `blog/my-post.md`
|
|
|
|
**All matching styles load (in order):**
|
|
|
|
1. **Base style** (always)
|
|
```
|
|
/styles/base.css
|
|
```
|
|
|
|
2. **Type + extension** styles (from root to specific)
|
|
```
|
|
/styles/__file.md.css
|
|
/styles/blog/__file.md.css
|
|
```
|
|
|
|
3. **Type + category** styles
|
|
```
|
|
/styles/__file.document.css
|
|
/styles/blog/__file.document.css
|
|
```
|
|
|
|
4. **Specific file** style
|
|
```
|
|
/styles/blog/my-post.md.css
|
|
```
|
|
|
|
**All found styles are included!**
|
|
|
|
### Rendered HTML
|
|
|
|
```html
|
|
<link rel="stylesheet" href="/styles/base.css">
|
|
<link rel="stylesheet" href="/styles/__file.md.css">
|
|
<link rel="stylesheet" href="/styles/blog/__file.md.css">
|
|
```
|
|
|
|
## Style Naming Patterns
|
|
|
|
### File Styles
|
|
|
|
Pattern: `__file.{extension}.css` or `__file.{category}.css`
|
|
|
|
```
|
|
styles/
|
|
├── __file.md.css # All markdown files
|
|
├── __file.document.css # All document files
|
|
├── __file.image.css # Individual images (rare)
|
|
└── __file.other.css # Other file types
|
|
```
|
|
|
|
### Folder Styles
|
|
|
|
Pattern: `__folder.{category}.css`
|
|
|
|
```
|
|
styles/
|
|
├── __folder.md.css # Folders with markdown
|
|
├── __folder.image.css # Photo galleries
|
|
└── __folder.html # Any folder view
|
|
```
|
|
|
|
### Specific Page Styles
|
|
|
|
Pattern: `{path/to/file}.css`
|
|
|
|
```
|
|
styles/
|
|
├── index.md.css # Only homepage
|
|
├── about.md.css # Only about page
|
|
└── blog/
|
|
└── special-post.md.css # One specific post
|
|
```
|
|
|
|
## Directory Structure
|
|
|
|
### Basic Structure
|
|
|
|
```
|
|
styles/
|
|
├── base.css # Required: Base styles
|
|
├── __file.md.css # Markdown file styles
|
|
└── __folder.image.css # Gallery styles
|
|
```
|
|
|
|
### Advanced Structure
|
|
|
|
```
|
|
styles/
|
|
├── base.css # Base
|
|
├── __file.md.css # General markdown
|
|
├── __folder.image.css # General galleries
|
|
├── blog/
|
|
│ ├── __file.md.css # Blog posts
|
|
│ └── __folder.md.css # Blog index
|
|
├── docs/
|
|
│ └── __file.md.css # Documentation
|
|
├── layouts/
|
|
│ ├── document.css # Document layout
|
|
│ ├── gallery.css # Gallery layout
|
|
│ └── landing.css # Landing pages
|
|
└── components/
|
|
├── navigation.css # Navigation
|
|
├── footer.css # Footer
|
|
└── breadcrumbs.css # Breadcrumbs
|
|
```
|
|
|
|
## Cascade & Specificity
|
|
|
|
### CSS Cascade Order
|
|
|
|
**Load order matters:**
|
|
|
|
1. `base.css` - Loaded first
|
|
2. General styles (`__file.md.css`)
|
|
3. Section styles (`blog/__file.md.css`)
|
|
4. Specific styles (`blog/my-post.md.css`)
|
|
|
|
**Later styles override earlier ones** (standard CSS behavior).
|
|
|
|
### Example Cascade
|
|
|
|
**Given page:** `blog/tutorial.md`
|
|
|
|
**Loaded styles:**
|
|
```html
|
|
<link rel="stylesheet" href="/styles/base.css">
|
|
<link rel="stylesheet" href="/styles/__file.md.css">
|
|
<link rel="stylesheet" href="/styles/blog/__file.md.css">
|
|
```
|
|
|
|
**base.css:**
|
|
```css
|
|
h1 {
|
|
color: black; /* Default */
|
|
}
|
|
```
|
|
|
|
**__file.md.css:**
|
|
```css
|
|
h1 {
|
|
color: #333; /* Slightly lighter */
|
|
}
|
|
```
|
|
|
|
**blog/__file.md.css:**
|
|
```css
|
|
h1 {
|
|
color: #0066cc; /* Blue - wins! */
|
|
}
|
|
```
|
|
|
|
**Result:** Blog headings are blue.
|
|
|
|
## Common Patterns
|
|
|
|
### Pattern 1: Base + Overrides
|
|
|
|
Start with comprehensive base, override as needed:
|
|
|
|
**base.css** - Everything
|
|
```css
|
|
/* Typography */
|
|
body { font-family: sans-serif; }
|
|
h1 { font-size: 2.5rem; }
|
|
h2 { font-size: 2rem; }
|
|
|
|
/* Layout */
|
|
.container { max-width: 1200px; }
|
|
|
|
/* Components */
|
|
nav { /* navigation styles */ }
|
|
footer { /* footer styles */ }
|
|
```
|
|
|
|
**blog/__file.md.css** - Blog-specific
|
|
```css
|
|
/* Override heading colors for blog */
|
|
h1 { color: #0066cc; }
|
|
|
|
/* Add blog-specific components */
|
|
.post-meta { /* metadata styles */ }
|
|
```
|
|
|
|
### Pattern 2: Modular Components
|
|
|
|
Split styles into reusable modules:
|
|
|
|
**base.css** - Minimal
|
|
```css
|
|
@import url('components/typography.css');
|
|
@import url('components/layout.css');
|
|
@import url('components/navigation.css');
|
|
```
|
|
|
|
**components/typography.css**
|
|
```css
|
|
body {
|
|
font-family: Georgia, serif;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
h1 { font-size: 2.5rem; }
|
|
/* ... */
|
|
```
|
|
|
|
**components/navigation.css**
|
|
```css
|
|
nav {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
/* ... */
|
|
```
|
|
|
|
### Pattern 3: Layout Variants
|
|
|
|
Different layouts for different sections:
|
|
|
|
**layouts/document.css**
|
|
```css
|
|
.document-layout {
|
|
display: grid;
|
|
grid-template-columns: 250px 1fr;
|
|
gap: 2rem;
|
|
}
|
|
|
|
.docs-sidebar { /* sidebar styles */ }
|
|
.docs-content { /* content styles */ }
|
|
```
|
|
|
|
**layouts/gallery.css**
|
|
```css
|
|
.gallery {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
gap: 1rem;
|
|
}
|
|
|
|
.photo-item { /* photo card styles */ }
|
|
```
|
|
|
|
**Include in templates:**
|
|
```html
|
|
<!-- __file.md.html for docs -->
|
|
<link rel="stylesheet" href="/styles/layouts/document.css">
|
|
<div class="document-layout">
|
|
<!-- content -->
|
|
</div>
|
|
```
|
|
|
|
### Pattern 4: Responsive Design
|
|
|
|
Mobile-first approach:
|
|
|
|
```css
|
|
/* base.css - Mobile first */
|
|
body {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.container {
|
|
display: block;
|
|
}
|
|
|
|
/* Tablet */
|
|
@media (min-width: 768px) {
|
|
body {
|
|
padding: 2rem;
|
|
}
|
|
|
|
.container {
|
|
display: grid;
|
|
grid-template-columns: 1fr 300px;
|
|
}
|
|
}
|
|
|
|
/* Desktop */
|
|
@media (min-width: 1200px) {
|
|
body {
|
|
padding: 3rem;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Practical Examples
|
|
|
|
### Example 1: Blog Site
|
|
|
|
```
|
|
styles/
|
|
├── base.css # Site-wide styles
|
|
├── __file.md.css # General markdown
|
|
├── __folder.md.css # Folder listings
|
|
└── blog/
|
|
├── __file.md.css # Blog posts
|
|
└── __folder.md.css # Blog index
|
|
```
|
|
|
|
**base.css:**
|
|
```css
|
|
/* Basic layout and typography */
|
|
body {
|
|
font-family: Georgia, serif;
|
|
line-height: 1.6;
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
padding: 2rem;
|
|
}
|
|
|
|
a { color: #0066cc; }
|
|
```
|
|
|
|
**__file.md.css:**
|
|
```css
|
|
/* Default markdown styles */
|
|
article {
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
code {
|
|
background: #f4f4f4;
|
|
padding: 0.2em 0.4em;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
pre {
|
|
background: #f4f4f4;
|
|
padding: 1rem;
|
|
overflow-x: auto;
|
|
}
|
|
```
|
|
|
|
**blog/__file.md.css:**
|
|
```css
|
|
/* Blog post specific */
|
|
.post-header {
|
|
border-bottom: 2px solid #eee;
|
|
padding-bottom: 1rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.post-meta {
|
|
color: #666;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.post-tags {
|
|
margin-top: 2rem;
|
|
}
|
|
|
|
.tag {
|
|
display: inline-block;
|
|
padding: 0.25rem 0.75rem;
|
|
background: #f0f0f0;
|
|
border-radius: 3px;
|
|
margin-right: 0.5rem;
|
|
font-size: 0.85rem;
|
|
}
|
|
```
|
|
|
|
**blog/__folder.md.css:**
|
|
```css
|
|
/* Blog index */
|
|
.post-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
}
|
|
|
|
.post-item {
|
|
margin: 2rem 0;
|
|
padding: 1.5rem;
|
|
border: 1px solid #eee;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.post-item:hover {
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
}
|
|
```
|
|
|
|
### Example 2: Photo Gallery
|
|
|
|
```
|
|
styles/
|
|
├── base.css
|
|
└── galleries/
|
|
└── __folder.image.css
|
|
```
|
|
|
|
**galleries/__folder.image.css:**
|
|
```css
|
|
.photo-gallery {
|
|
padding: 2rem;
|
|
}
|
|
|
|
.photo-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
gap: 1.5rem;
|
|
margin-top: 2rem;
|
|
}
|
|
|
|
.photo-item {
|
|
position: relative;
|
|
overflow: hidden;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
}
|
|
|
|
.photo-item:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
|
}
|
|
|
|
.photo-item img {
|
|
width: 100%;
|
|
height: auto;
|
|
display: block;
|
|
}
|
|
|
|
.photo-caption {
|
|
padding: 0.75rem;
|
|
background: white;
|
|
font-size: 0.85rem;
|
|
color: #666;
|
|
}
|
|
|
|
/* Responsive */
|
|
@media (max-width: 768px) {
|
|
.photo-grid {
|
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
gap: 1rem;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Example 3: Documentation Site
|
|
|
|
```
|
|
styles/
|
|
├── base.css
|
|
├── layouts/
|
|
│ └── document.css
|
|
└── docs/
|
|
└── __file.md.css
|
|
```
|
|
|
|
**layouts/document.css:**
|
|
```css
|
|
.docs-layout {
|
|
display: grid;
|
|
grid-template-columns: 250px 1fr;
|
|
gap: 3rem;
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.docs-sidebar {
|
|
position: sticky;
|
|
top: 2rem;
|
|
height: calc(100vh - 4rem);
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.docs-content {
|
|
min-width: 0; /* Prevent grid blowout */
|
|
}
|
|
|
|
@media (max-width: 1024px) {
|
|
.docs-layout {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.docs-sidebar {
|
|
position: static;
|
|
height: auto;
|
|
}
|
|
}
|
|
```
|
|
|
|
**docs/__file.md.css:**
|
|
```css
|
|
.documentation {
|
|
max-width: 800px;
|
|
}
|
|
|
|
/* Table of contents */
|
|
.doc-toc {
|
|
background: #f8f8f8;
|
|
padding: 1rem;
|
|
border-radius: 4px;
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
/* Code blocks */
|
|
.documentation pre {
|
|
background: #282c34;
|
|
color: #abb2bf;
|
|
padding: 1.5rem;
|
|
border-radius: 6px;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.documentation code {
|
|
font-family: 'Monaco', 'Courier New', monospace;
|
|
}
|
|
|
|
/* Callouts */
|
|
.note,
|
|
.warning,
|
|
.tip {
|
|
padding: 1rem 1.5rem;
|
|
margin: 1.5rem 0;
|
|
border-left: 4px solid;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.note {
|
|
background: #e3f2fd;
|
|
border-color: #2196f3;
|
|
}
|
|
|
|
.warning {
|
|
background: #fff3e0;
|
|
border-color: #ff9800;
|
|
}
|
|
|
|
.tip {
|
|
background: #e8f5e9;
|
|
border-color: #4caf50;
|
|
}
|
|
```
|
|
|
|
## CSS Variables
|
|
|
|
Use CSS custom properties for theming:
|
|
|
|
### base.css with Variables
|
|
|
|
```css
|
|
:root {
|
|
/* Colors */
|
|
--color-primary: #0066cc;
|
|
--color-secondary: #6c757d;
|
|
--color-text: #333;
|
|
--color-background: #fff;
|
|
--color-border: #dee2e6;
|
|
|
|
/* Typography */
|
|
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
|
|
--font-serif: Georgia, serif;
|
|
--font-mono: 'Monaco', 'Courier New', monospace;
|
|
|
|
/* Spacing */
|
|
--space-xs: 0.5rem;
|
|
--space-sm: 1rem;
|
|
--space-md: 2rem;
|
|
--space-lg: 3rem;
|
|
|
|
/* Breakpoints (for reference) */
|
|
/* Use in @media queries */
|
|
}
|
|
|
|
body {
|
|
color: var(--color-text);
|
|
background: var(--color-background);
|
|
font-family: var(--font-sans);
|
|
}
|
|
|
|
a {
|
|
color: var(--color-primary);
|
|
}
|
|
|
|
/* ... */
|
|
```
|
|
|
|
### Dark Mode
|
|
|
|
```css
|
|
/* base.css */
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
--color-text: #e4e4e4;
|
|
--color-background: #1a1a1a;
|
|
--color-border: #333;
|
|
}
|
|
}
|
|
|
|
/* Or toggle with class */
|
|
body.dark-mode {
|
|
--color-text: #e4e4e4;
|
|
--color-background: #1a1a1a;
|
|
--color-border: #333;
|
|
}
|
|
```
|
|
|
|
## Performance Tips
|
|
|
|
### 1. Minimize File Size
|
|
|
|
```css
|
|
/* Remove unnecessary spaces/newlines in production */
|
|
/* Use a CSS minifier */
|
|
```
|
|
|
|
### 2. Avoid @import
|
|
|
|
```css
|
|
/* Slow - additional HTTP request */
|
|
@import url('components/typography.css');
|
|
|
|
/* Better - combine files or use build tool */
|
|
/* Or let browser load multiple <link> tags in parallel */
|
|
```
|
|
|
|
### 3. Optimize Selectors
|
|
|
|
```css
|
|
/* Fast */
|
|
.class-name { }
|
|
#id-name { }
|
|
|
|
/* Slower */
|
|
div > ul > li > a { }
|
|
[data-attribute="value"] { }
|
|
|
|
/* Use classes for styling */
|
|
```
|
|
|
|
### 4. Use Will-Change Sparingly
|
|
|
|
```css
|
|
/* Only for elements that will actually animate */
|
|
.photo-item {
|
|
transition: transform 0.2s;
|
|
}
|
|
|
|
.photo-item:hover {
|
|
transform: translateY(-4px);
|
|
will-change: transform; /* Hint to browser */
|
|
}
|
|
```
|
|
|
|
## Debugging Styles
|
|
|
|
### Browser DevTools
|
|
|
|
1. **Inspect element** - Right-click → Inspect
|
|
2. **Check computed styles** - See which rules apply
|
|
3. **See cascade** - Understand override order
|
|
4. **Live edit** - Test changes instantly
|
|
|
|
### Debug Checklist
|
|
|
|
**Styles not loading:**
|
|
- [ ] File exists in `styles/` directory
|
|
- [ ] Filename matches expected pattern
|
|
- [ ] No syntax errors in CSS
|
|
- [ ] Browser cache cleared
|
|
|
|
**Styles not applying:**
|
|
- [ ] Check CSS specificity
|
|
- [ ] Check cascade order
|
|
- [ ] Look for typos in selectors
|
|
- [ ] Verify HTML classes match CSS
|
|
|
|
**Wrong styles applying:**
|
|
- [ ] Check for conflicting rules
|
|
- [ ] Verify file loading order
|
|
- [ ] Look for !important (avoid if possible)
|
|
|
|
## Next Steps
|
|
|
|
- **[Templates Guide](../templates/)** - Templates use these styles
|
|
- **[Template Discovery](../templates/template-discovery.md)** - How styles are discovered
|
|
- **[Recipes](../recipes/)** - Complete examples with CSS
|
|
|
|
Master the style system to create beautiful, maintainable Foldsites!
|