---
title: "Visual Design System"
subtitle: "Color schemes, Plotly templates, and styling conventions"
description: "Reference for book-wide styling, UI components, and visualization standards"
---
# Introduction
The Aquifer Intelligence Playbook uses a **custom visual design system** to ensure consistency across all visualizations, documentation, and interactive components. This guide provides reference for extending chapters or building new artifacts that align with our visual standards.
**This guide covers:**
- Color palettes and theme tokens
- Plotly visualization templates
- Quarto book styling conventions
- Reusable UI components
- Figure and asset organization
---
## Color Palettes
### Primary Palette (Book-Wide)
The base color scheme reflects water and earth tones:
| Token | Hex | Usage |
|-------|-----|-------|
| `--aquifer-navy` | `#1b2a4b` | Headings, primary text, high-contrast elements |
| `--aquifer-sky` | `#3cb4e5` | Accents, hyperlinks, interactive elements |
| `--aquifer-mint` | `#4bd5b3` | Highlights, success states, positive indicators |
| `--aquifer-sand` | `#f5f7fa` | Background, light surfaces |
| `--aquifer-steel` | `#64748b` | Secondary text, borders |
**Usage Guidelines:**
- Use `--aquifer-navy` for high contrast and readability
- Reserve `--aquifer-sky` for clickable/interactive elements
- Use `--aquifer-mint` sparingly for emphasis
- `--aquifer-sand` provides subtle backgrounds
---
### Part-Specific Colors
Each part of the playbook has a unique accent color for visual navigation:
| Part | Color Name | Hex | Usage |
|------|-----------|-----|-------|
| Part 1 | Azure Blue | `#2e8bcc` | Foundations - Individual analysis |
| Part 2 | Lagoon Teal | `#18b8c9` | Spatial Patterns - 2D/3D analysis |
| Part 3 | Mint Green | `#3cd4a8` | Temporal Dynamics - Time series |
| Part 4 | Purple | `#7c3aed` | Data Fusion - Integration |
| Part 5 | Amber | `#f59e0b` | Operations - Production systems |
| Part 6 | Pink | `#ec4899` | Knowledge - Synthesis & learning |
| Assets | Slate Gray | `#64748b` | Shared resources |
**Implementation:**
- Colors automatically applied via `part-colors.js`
- Affects navigation, table of contents, and in-page elements
- No manual color coding required in chapters
---
## HTEM Visualization Scales
### Material Type Color Scale
For HTEM material type visualizations:
```python
# Sediment Quality Scale (Low to High)
material_colors = {
1: '#8B4513', # Clay - Dark brown
2: '#A0522D', # Very poorly sorted - Sienna
3: '#BC8F8F', # Fine diamicton - Rosy brown
5: '#D2B48C', # Poorly sorted - Tan
8: '#F4A460', # Moderately sorted - Sandy brown
11: '#FFD700', # Well sorted - Gold
13: '#FFA500', # Very well sorted - Orange
14: '#FF8C00' # Extremely well sorted - Dark orange
}
# Bedrock Scale
bedrock_colors = {
101: '#696969', # Carboniferous shale
102: '#778899', # Carboniferous sandstone
103: '#A9A9A9', # Carboniferous margins
104: '#C0C0C0', # Carbonate margins
105: '#D3D3D3' # Carbonate
}
```
---
### Resistivity Color Scale
For continuous resistivity visualizations (Plotly):
```python
# Diverging scale for resistivity values
resistivity_colorscale = [
[0.0, '#2c3e50'], # Low (Clay) - Dark blue-gray
[0.2, '#3498db'], # Low-medium - Blue
[0.4, '#1abc9c'], # Medium - Teal
[0.6, '#f39c12'], # Medium-high - Orange
[0.8, '#e74c3c'], # High - Red
[1.0, '#c0392b'] # Very high (Bedrock) - Dark red
]
```
**Usage in Plotly:**
```python
import plotly.graph_objects as go
fig = go.Figure(data=go.Heatmap(
z=resistivity_data,
colorscale=resistivity_colorscale,
colorbar=dict(title='Resistivity (Ω·m)')
))
```
---
## Plotly Visualization Templates
### Standard Template
Use this template for all Plotly visualizations to ensure consistency:
```python
import plotly.graph_objects as go
import plotly.io as pio
# Define custom template
aquifer_template = go.layout.Template(
layout=dict(
# Fonts
font=dict(
family="Inter, system-ui, -apple-system, sans-serif",
size=12,
color="#1b2a4b"
),
title=dict(
font=dict(size=18, color="#1b2a4b"),
x=0.5,
xanchor='center'
),
# Background colors
paper_bgcolor="#ffffff",
plot_bgcolor="#f5f7fa",
# Grid
xaxis=dict(
gridcolor="#e2e8f0",
showgrid=True,
zeroline=False
),
yaxis=dict(
gridcolor="#e2e8f0",
showgrid=True,
zeroline=False
),
# Color sequence
colorway=['#2e8bcc', '#18b8c9', '#3cd4a8', '#7c3aed', '#f59e0b', '#ec4899'],
# Margins
margin=dict(l=60, r=40, t=60, b=60),
# Modebar
modebar=dict(
bgcolor='rgba(255,255,255,0.8)',
color='#64748b',
activecolor='#2e8bcc'
)
)
)
# Register template
pio.templates["aquifer"] = aquifer_template
pio.templates.default = "aquifer"
```
**Usage**: Copy this template into your analysis scripts or save to a local `plotly_theme.py` file. This provides a reference implementationβadapt as needed for your specific visualizations.
---
### 2D Spatial Plot Template
For HTEM grid visualizations:
```python
def create_spatial_plot(x, y, z, title, colorbar_title):
"""Create standardized 2D spatial plot."""
fig = go.Figure(data=go.Heatmap(
x=x,
y=y,
z=z,
colorscale=resistivity_colorscale,
colorbar=dict(
title=colorbar_title,
titleside='right',
tickmode='linear',
tick0=0,
dtick=50
)
))
fig.update_layout(
title=title,
xaxis_title="Easting (m)",
yaxis_title="Northing (m)",
xaxis=dict(scaleanchor="y", scaleratio=1), # Equal aspect ratio
width=800,
height=700
)
return fig
```
---
### Time Series Plot Template
For temporal analysis:
```python
def create_timeseries_plot(dates, values, title, y_label):
"""Create standardized time series plot."""
fig = go.Figure()
fig.add_trace(go.Scatter(
x=dates,
y=values,
mode='lines',
line=dict(color='#2e8bcc', width=2),
name='Observed'
))
fig.update_layout(
title=title,
xaxis_title="Date",
yaxis_title=y_label,
hovermode='x unified',
showlegend=True,
width=1000,
height=500
)
return fig
```
---
### 3D Visualization Template
For subsurface models:
```python
def create_3d_plot(x, y, z, values, title):
"""Create standardized 3D scatter plot."""
fig = go.Figure(data=go.Scatter3d(
x=x,
y=y,
z=z,
mode='markers',
marker=dict(
size=2,
color=values,
colorscale=resistivity_colorscale,
showscale=True,
colorbar=dict(title='Resistivity (Ω·m)')
)
))
fig.update_layout(
title=title,
scene=dict(
xaxis_title="Easting (m)",
yaxis_title="Northing (m)",
zaxis_title="Elevation (m)",
aspectmode='data'
),
width=900,
height=700
)
return fig
```
---
## Quarto Book Styling
### CSS Variables
Defined in `aquifer-book/styles.css`:
```css
:root {
/* Colors */
--aquifer-navy: #1b2a4b;
--aquifer-sky: #3cb4e5;
--aquifer-mint: #4bd5b3;
--aquifer-sand: #f5f7fa;
--aquifer-steel: #64748b;
/* Typography */
--font-family-sans: "Inter", system-ui, -apple-system, sans-serif;
--font-family-mono: "Fira Code", "Consolas", monospace;
/* Spacing */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
/* Border radius */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
}
```
---
### Custom Components
#### Aquifer Banner
Use for chapter introductions:
```markdown
::: {.aquifer-banner}
# Chapter Title
Brief description of what this chapter covers and why it matters.
:::
```
**CSS:**
```css
.aquifer-banner {
background: linear-gradient(135deg, var(--aquifer-navy) 0%, var(--aquifer-sky) 100%);
color: white;
padding: var(--spacing-xl);
border-radius: var(--radius-lg);
margin-bottom: var(--spacing-lg);
}
```
---
#### Workflow Grid
For displaying phase overviews or multi-step processes:
```markdown
::: {.workflow-grid}
::: {.workflow-card}
### Step 1
Description
:::
::: {.workflow-card}
### Step 2
Description
:::
::: {.workflow-card}
### Step 3
Description
:::
:::
```
**CSS:**
```css
.workflow-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--spacing-md);
margin: var(--spacing-lg) 0;
}
.workflow-card {
background: var(--aquifer-sand);
border: 1px solid #e2e8f0;
border-radius: var(--radius-md);
padding: var(--spacing-md);
}
```
---
#### Callout Styles
Standard Quarto callouts with custom styling:
```markdown
::: {.callout-note icon=false}
## π» For Computer Scientists
CS-specific content here
:::
::: {.callout-tip icon=false}
## π For Hydrogeologists
Hydro-specific content here
:::
::: {.callout-important icon=false}
## π For Statisticians
Stats-specific content here
:::
::: {.callout-warning icon=false}
## β οΈ Critical Issue
Important warnings
:::
```
---
### Code Block Styling
Syntax highlighting uses **Monokai** theme with custom tweaks:
```yaml
# In _quarto.yml
format:
html:
highlight-style: monokai
code-copy: true
code-line-numbers: true
```
**Custom code block additions:**
```css
pre.sourceCode {
background: #272822 !important;
border-radius: var(--radius-md);
padding: var(--spacing-md);
}
code {
font-family: var(--font-family-mono);
font-size: 0.9em;
}
```
---
## Figure Organization
### Directory Structure
```
aquifer-book/
βββ assets/
β βββ figures/
β βββ part-1/ # Foundations figures
β βββ part-2/ # Spatial figures
β βββ part-3/ # Temporal figures
β βββ part-4/ # Fusion figures
β βββ part-5/ # Operations figures
β βββ part-6/ # Knowledge figures
β βββ shared/ # Cross-cutting figures
```
---
### Naming Conventions
**Static images:**
```
{part}-{chapter}-{description}-{version}.{ext}
Examples:
- part1-htem-resistivity-distribution-v1.png
- part2-spatial-variogram-analysis-v2.png
- part3-timeseries-decomposition-final.png
```
**Interactive HTML figures:**
```
{part}-{chapter}-{description}-interactive.html
Examples:
- part2-spatial-3d-aquifer-model-interactive.html
- part4-fusion-correlation-matrix-interactive.html
```
---
### Figure Metadata
Include metadata table in chapters for reproducibility:
```markdown
| Figure | Source Script | Data Version | Last Updated |
|--------|--------------|--------------|--------------|
| Figure 2.1 | `scripts/spatial_analysis.py` | 2024-2025 | 2025-10-31 |
| Figure 2.2 | `notebooks/02_Spatial_Stats.ipynb` | 2024-2025 | 2025-10-30 |
```
---
## Interactive Component Guidelines
### Plotly Interactivity
**Standard modebar buttons:**
```python
fig.update_layout(
modebar=dict(
buttons=[
'zoom2d', 'pan2d', 'zoomIn2d', 'zoomOut2d',
'autoScale2d', 'resetScale2d',
'toImage'
]
)
)
```
**Hover templates:**
```python
hovertemplate = (
"<b>Location</b>: (%{x:.0f}, %{y:.0f})<br>"
"<b>Resistivity</b>: %{z:.1f} Ω·m<br>"
"<extra></extra>" # Removes trace name
)
fig.update_traces(hovertemplate=hovertemplate)
```
---
### Responsive Design
Ensure visualizations work on different screen sizes:
```python
fig.update_layout(
autosize=True,
width=None, # Let container define width
height=600, # Fixed height for consistency
# Mobile-friendly margins
margin=dict(
l=50 if is_mobile else 60,
r=30 if is_mobile else 40,
t=50 if is_mobile else 60,
b=50 if is_mobile else 60
)
)
```
---
## Typography Standards
### Headings
```markdown
# Part Title (H1)
## Chapter Title (H2)
### Major Section (H3)
#### Subsection (H4)
##### Minor Subsection (H5)
```
**Guidelines:**
- H1 only for part titles
- H2 for chapter titles
- H3 for major sections within chapters
- Avoid going deeper than H5
---
### Text Formatting
**Emphasis:**
- `**bold**` for important terms on first use
- `*italic*` for emphasis or introducing new concepts
- `` `code` `` for variable names, function names, file paths
**Lists:**
- Use bullet points for unordered lists
- Use numbered lists for sequential steps
- Indent nested lists consistently
**Links:**
```markdown
[Descriptive link text](url)
[See Temporal Overview](../part-3-temporal/overview.qmd)
```
---
## Accessibility Standards
::: {.callout-note icon=false}
## For Newcomers: Why Accessibility Matters
**Accessibility** means designing so everyone can use the playbook, regardless of:
- Visual abilities (color blindness, low vision, blindness)
- Motor abilities (using keyboard vs. mouse)
- Cognitive preferences (different learning styles)
**Good accessibility benefits everyone:**
- High contrast text is easier to read for all users
- Keyboard navigation helps power users work faster
- Alt text helps when images don't load
- Clear descriptions help when you're learning complex topics
:::
### Color Contrast
All color combinations meet **WCAG 2.1 AA standards**:
- Minimum contrast ratio: 4.5:1 for normal text
- Minimum contrast ratio: 3:1 for large text
- `--aquifer-navy` on white: 12.6:1 β
- `--aquifer-sky` on white: 3.8:1 β (large text only)
::: {.callout-tip icon=false}
## What This Means
**Contrast ratio** measures how easy it is to distinguish text from background:
- **Higher ratio = easier to read** (even in bright sunlight or for people with low vision)
- **WCAG AA** is an international standard for web accessibility
- **4.5:1 minimum** ensures most people can read normal-sized text
- **3:1 for large text** (18pt+ or 14pt+ bold) is acceptable for headings
**Our navy text on white background (12.6:1)** exceeds minimum by 2.8Γ, making it very easy to read.
:::
---
### Alternative Text
**Always provide alt text for images:**
```markdown

```
**For complex figures, provide detailed descriptions:**
```markdown
::: {.figure-description}
**Figure 2.3** shows a 2D cross-section of resistivity values from 0-100m depth.
High resistivity zones (>100 Ω·m, shown in orange-red) indicate sandy aquifer
materials at 15-25m depth. Low resistivity zones (<30 Ω·m, shown in blue)
indicate clay-rich confining layers.
:::
```
---
### Keyboard Navigation
Ensure all interactive elements are keyboard-accessible:
- Plotly charts: Use built-in keyboard support
- Custom buttons: Add `tabindex="0"` and keyboard event handlers
- Navigation: Test with Tab, Enter, Escape keys
---
## Export Formats
### Figure Export Settings
**For publication:**
```python
fig.write_image(
"figure.png",
width=1200,
height=800,
scale=2 # 2x resolution for high DPI displays
)
fig.write_image(
"figure.pdf",
width=1200,
height=800
)
```
**For web:**
```python
fig.write_html(
"figure.html",
include_plotlyjs='cdn', # Smaller file size
config={'displayModeBar': True}
)
```
---
## Extending the Theme
### Adding New Colors
1. Add to CSS variables in `styles.css`
2. Document in this guide
3. Update `plotly_theme.py` if applicable
4. Test contrast ratios
### Creating New Components
1. Design in HTML/CSS
2. Add to `styles.css`
3. Document usage in this guide
4. Provide example in dedicated demo chapter
### Modifying Plotly Template
1. Edit `src/visualization/plotly_theme.py`
2. Test across all visualization types
3. Update this guide with changes
4. Regenerate affected figures
---
## Common Patterns
### Side-by-Side Figures
```markdown
::: {.grid}
::: {.g-col-6}

:::
::: {.g-col-6}

:::
:::
```
---
### Figure with Caption and Source Attribution
```markdown

**Figure 2.5.** Distribution of resistivity values in Unit D (primary aquifer).
High-resistivity tail (>150 Ω·m) indicates well-sorted sand bodies.
*Source:* `scripts/comprehensive_htem_analysis.py`, Module 3
```
---
### Multi-Panel Dashboard Layout
```python
from plotly.subplots import make_subplots
fig = make_subplots(
rows=2, cols=2,
subplot_titles=('Panel A', 'Panel B', 'Panel C', 'Panel D'),
specs=[
[{'type': 'scatter'}, {'type': 'bar'}],
[{'type': 'heatmap', 'colspan': 2}, None]
]
)
# Add traces to each panel
fig.add_trace(go.Scatter(...), row=1, col=1)
fig.add_trace(go.Bar(...), row=1, col=2)
fig.add_trace(go.Heatmap(...), row=2, col=1)
fig.update_layout(height=800, showlegend=False)
```
---
## Quality Checklist
Before committing visualizations, verify:
- [ ] Colors use defined palette or template
- [ ] Fonts are consistent with theme
- [ ] Axis labels are clear and include units
- [ ] Figure has descriptive title
- [ ] Legend is present if needed
- [ ] Hover templates are informative
- [ ] Figure is responsive (tests on mobile)
- [ ] Alt text provided for static images
- [ ] Color contrast meets WCAG AA
- [ ] File naming follows convention
- [ ] Metadata documented if needed
---
## Resources
### Design Tools
- **Color Contrast Checker:** [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
- **Palette Generator:** [Coolors.co](https://coolors.co)
- **Plotly Documentation:** [plotly.com/python](https://plotly.com/python/)
### Inspiration
- **Matplotlib Gallery:** For layout ideas
- **Observable HQ:** For interactive dashboard concepts
- **R Shiny Gallery:** For application design patterns
---
## Contributing to the Theme
Found a better color scheme? Want to add a new component? Contributions welcome!
**Process:**
1. Prototype your change
2. Test across multiple chapters
3. Update this guide with documentation
4. Submit PR with examples
**Questions?** Open an issue with the `design` label.
---
**Last Updated:** November 26, 2025
**Maintainers:** Design team (open to contributors)
**License:** MIT (same as project)