Agent skill

policyengine-design

PolicyEngine visual identity - colors, fonts, logos, and branding for web apps, calculators, charts, and research

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/policyengine-design

SKILL.md

PolicyEngine Design System

PolicyEngine's visual identity and branding guidelines for creating consistent user experiences across web apps, calculators, charts, and research outputs.

For Users 👥

PolicyEngine Visual Identity

Brand colors:

  • Teal (#39C6C0) - Primary accent color (buttons, highlights, interactive elements)
  • Blue (#2C6496) - Secondary color (links, charts, headers)

Typography:

  • Charts: Roboto Serif
  • Web app: System fonts (sans-serif)
  • Streamlit apps: Default sans-serif

Logo:

  • Used in charts (bottom right)
  • Blue version for light backgrounds
  • White version for dark backgrounds

Recognizing PolicyEngine Content

You can identify PolicyEngine content by:

  • Teal accent color (#39C6C0) on buttons and interactive elements
  • Blue (#2C6496) in charts and links
  • Roboto Serif font in charts
  • PolicyEngine logo in chart footer
  • Clean, minimal white backgrounds
  • Data-focused, quantitative presentation

For Analysts 📊

Chart Branding

When creating charts for PolicyEngine analysis, follow these guidelines:

Color Palette

Primary colors:

python
TEAL_ACCENT = "#39C6C0"   # Primary color (teal)
BLUE_PRIMARY = "#2C6496"  # Secondary color (blue)
DARK_GRAY = "#616161"     # Text color

Extended palette:

python
# Blues
BLUE = "#2C6496"
BLUE_LIGHT = "#D8E6F3"
BLUE_PRESSED = "#17354F"
BLUE_98 = "#F7FAFD"
DARK_BLUE_HOVER = "#1d3e5e"
DARKEST_BLUE = "#0C1A27"

# Teals
TEAL_ACCENT = "#39C6C0"
TEAL_LIGHT = "#F7FDFC"
TEAL_PRESSED = "#227773"

# Grays
DARK_GRAY = "#616161"
GRAY = "#808080"
MEDIUM_LIGHT_GRAY = "#BDBDBD"
MEDIUM_DARK_GRAY = "#D2D2D2"
LIGHT_GRAY = "#F2F2F2"

# Accents
WHITE = "#FFFFFF"
BLACK = "#000000"
DARK_RED = "#b50d0d"  # For negative values

See current colors:

bash
cat policyengine-app/src/style/colors.js

Plotly Chart Formatting

Standard PolicyEngine chart:

python
import plotly.graph_objects as go

def format_fig(fig):
    """Format chart with PolicyEngine branding."""
    fig.update_layout(
        # Typography
        font=dict(
            family="Roboto Serif",
            color="black"
        ),

        # Background
        plot_bgcolor="white",
        template="plotly_white",

        # Margins (leave room for logo)
        margin=dict(
            l=50,
            r=100,
            t=50,
            b=120,
            pad=4
        ),

        # Chart size
        height=600,
        width=800,
    )

    # Add PolicyEngine logo (bottom right)
    fig.add_layout_image(
        dict(
            source="https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/teal.png",
            xref="paper",
            yref="paper",
            x=1.0,
            y=-0.10,
            sizex=0.10,
            sizey=0.10,
            xanchor="right",
            yanchor="bottom"
        )
    )

    # Clean modebar
    fig.update_layout(
        modebar=dict(
            bgcolor="rgba(0,0,0,0)",
            color="rgba(0,0,0,0)"
        )
    )

    return fig

# Usage
fig = go.Figure()
fig.add_trace(go.Scatter(x=x_data, y=y_data, line=dict(color=TEAL_ACCENT)))
fig = format_fig(fig)

Current implementation:

bash
# See format_fig in action
cat givecalc/ui/visualization.py
cat policyengine-app/src/pages/policy/output/...

Blog Post Charts (Standalone HTML)

When creating standalone Plotly HTML files for blog posts, use these specific settings:

Font: Use "Roboto" (not "Roboto Serif") with Google Fonts embed:

python
FONT_FAMILY = "Roboto, Arial, sans-serif"

# After generating HTML, embed font
html = fig.to_html(include_plotlyjs='cdn')
html_with_font = html.replace('</head>',
    '<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet"></head>')
with open('chart.html', 'w') as f:
    f.write(html_with_font)

Background: LIGHT_GRAY (#F2F2F2) for better blog integration:

python
fig.update_layout(
    plot_bgcolor="#F2F2F2",
    paper_bgcolor="#F2F2F2"
)

Logo positioning: x=0.95, y=-0.2 avoids cutoff:

python
images=[{
    "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/teal.png",
    "xref": "paper", "yref": "paper",
    "x": 0.95,  # Not 1.0 - prevents cutoff
    "y": -0.2,
    "sizex": 0.15, "sizey": 0.15,
    "xanchor": "right", "yanchor": "bottom"
}]

Margins: Increase right margin when x-axis extends to boundary:

python
# For charts showing $0-$2B or similar
margin=dict(t=120, b=120, l=120, r=80)  # r=80 ensures last tick visible

Grid and axes:

python
xaxis=dict(
    gridcolor="#FFFFFF",      # White grid on LIGHT_GRAY background
    zerolinecolor="#FFFFFF",
    tickformat='$,.1f',       # Units in tick labels
    ticksuffix='B'            # Not in axis title
),
yaxis=dict(
    gridcolor="#FFFFFF",
    zerolinecolor="#FFFFFF",
    ticksuffix='%'
)

Colors for reference lines: Use GRAY (#808080), not red:

python
# Reference/statutory lines
line=dict(color="#808080", width=2, dash='dash')

Stepwise patterns: Use 10,000+ points for discrete boundaries:

python
# For charts showing $2M increments
wealth = np.linspace(0, 2.0, 10000)  # Fine granularity
line=dict(shape='hv')  # Horizontal-vertical steps

Legend: Position inside chart when needed:

python
legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01)
# Or remove entirely if obvious: showlegend=False

Chart Colors

For line charts:

  • Primary line: Teal (#39C6C0) or Blue (#2C6496)
  • Background lines: Light gray (rgb(180, 180, 180))
  • Markers: Teal with 70% opacity

For bar charts:

  • Positive values: Teal (#39C6C0)
  • Negative values: Dark red (#b50d0d)
  • Neutral: Gray

For multiple series: Use variations of blue and teal, or discrete color scale:

python
colors = ["#2C6496", "#39C6C0", "#17354F", "#227773"]

Typography

Charts:

python
font=dict(family="Roboto Serif", size=14, color="black")

Axis labels:

python
xaxis=dict(
    title=dict(text="Label", font=dict(size=14)),
    tickfont=dict(size=12)
)

Load Roboto font:

python
# In Streamlit apps
st.markdown("""
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
""", unsafe_allow_html=True)

Streamlit App Branding

Streamlit configuration (.streamlit/config.toml):

toml
[theme]
base = "light"
primaryColor = "#39C6C0"          # Teal accent
backgroundColor = "#FFFFFF"        # White background
secondaryBackgroundColor = "#F7FDFC"  # Teal light
textColor = "#616161"              # Dark gray

[client]
toolbarMode = "minimal"

Current implementation:

bash
cat givecalc/.streamlit/config.toml
cat salt-amt-calculator/.streamlit/config.toml  # Other calculators

Logo Usage

Logo URLs (app-v2 - current):

python
# Teal logo (for light backgrounds) - PREFERRED
LOGO_TEAL = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/teal.png"
LOGO_TEAL_SQUARE = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/teal-square.png"

# White logo (for dark backgrounds)
LOGO_WHITE = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/white.png"

# SVG versions (scalable)
LOGO_TEAL_SVG = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/teal.svg"
LOGO_WHITE_SVG = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/white.svg"

Logo placement in charts:

  • Bottom right corner
  • 10% of chart width
  • Slightly below bottom edge (y=-0.10)

Current logos:

bash
ls policyengine-app-v2/app/public/assets/logos/policyengine/
# teal.png, teal.svg (wide)
# teal-square.png, teal-square.svg (square)
# white.png, white.svg (wide)
# white-square.svg (square)

Complete Example: Branded Chart

python
import plotly.graph_objects as go

# PolicyEngine colors
TEAL_ACCENT = "#39C6C0"
BLUE_PRIMARY = "#2C6496"

# Create chart
fig = go.Figure()

# Add data
fig.add_trace(go.Scatter(
    x=incomes,
    y=taxes,
    mode='lines',
    name='Tax liability',
    line=dict(color=TEAL_ACCENT, width=3)
))

# Apply PolicyEngine branding
fig.update_layout(
    # Typography
    font=dict(family="Roboto Serif", size=14, color="black"),

    # Title and labels
    title="Tax liability by income",
    xaxis_title="Income",
    yaxis_title="Tax ($)",

    # Formatting
    xaxis_tickformat="$,.0f",
    yaxis_tickformat="$,.0f",

    # Appearance
    plot_bgcolor="white",
    template="plotly_white",

    # Size and margins
    height=600,
    width=800,
    margin=dict(l=50, r=100, t=50, b=120, pad=4)
)

# Add logo
fig.add_layout_image(
    dict(
        source="https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/teal.png",
        xref="paper",
        yref="paper",
        x=1.0,
        y=-0.10,
        sizex=0.10,
        sizey=0.10,
        xanchor="right",
        yanchor="bottom"
    )
)

# Show
fig.show()

For Contributors 💻

Brand Assets

Repository: PolicyEngine/policyengine-app-v2 (current), PolicyEngine/policyengine-app (legacy)

Logo files:

bash
# Logos in app (both v1 and v2 use same logos)
ls policyengine-app/src/images/logos/policyengine/
# - blue.png - For light backgrounds
# - white.png - For dark backgrounds
# - blue.svg - Scalable blue logo
# - white.svg - Scalable white logo
# - banners/ - Banner variations
# - profile/ - Profile/avatar versions

Access logos:

bash
# View logo files (v1 repo has the assets)
cd policyengine-app/src/images/logos/policyengine/
ls -la

Color Definitions

⚠️ IMPORTANT: App V2 Transition

PolicyEngine is transitioning to policyengine-app-v2 with updated design tokens. Use app-v2 colors for new projects.

Current colors (policyengine-app-v2):

typescript
// policyengine-app-v2/app/src/designTokens/colors.ts

// Primary (teal) - 50 to 900 scale
primary[500]: "#319795"  // Main teal
primary[400]: "#38B2AC"  // Lighter teal
primary[600]: "#2C7A7B"  // Darker teal

// Blue scale
blue[700]: "#026AA2"     // Primary blue
blue[500]: "#0EA5E9"     // Lighter blue

// Gray scale
gray[700]: "#344054"     // Dark text
gray[100]: "#F2F4F7"     // Light backgrounds

// Semantic
success: "#22C55E"
warning: "#FEC601"
error: "#EF4444"

// Background
background.primary: "#FFFFFF"
background.secondary: "#F5F9FF"

// Text
text.primary: "#000000"
text.secondary: "#5A5A5A"

To see current design tokens:

bash
cat policyengine-app-v2/app/src/designTokens/colors.ts
cat policyengine-app-v2/app/src/styles/colors.ts  # Mantine integration

Legacy colors (policyengine-app - still used in some projects):

javascript
// policyengine-app/src/style/colors.js
TEAL_ACCENT = "#39C6C0"  // Old teal (slightly different from v2)
BLUE = "#2C6496"         // Old blue
DARK_GRAY = "#616161"    // Old dark gray

To see legacy colors:

bash
cat policyengine-app/src/style/colors.js

Usage in React (app-v2):

typescript
import { colors } from 'designTokens';

<Button style={{ backgroundColor: colors.primary[500] }} />
<Text style={{ color: colors.text.primary }} />

Usage in Python/Plotly (use legacy colors for now):

python
# For charts, continue using legacy colors until officially migrated
TEAL_ACCENT = "#39C6C0"  # From original app
BLUE_PRIMARY = "#2C6496"  # From original app

# Or use app-v2 colors
TEAL_PRIMARY = "#319795"  # From app-v2
BLUE_PRIMARY_V2 = "#026AA2"  # From app-v2

Typography

Fonts:

For charts (Plotly):

python
font=dict(family="Roboto Serif")

For web apps:

javascript
// System font stack
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, ...

Loading Google Fonts:

html
<!-- In Streamlit or HTML -->
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&family=Roboto+Serif:wght@300;400;500;700&display=swap" rel="stylesheet">

Chart Formatting Function

Reference implementation:

bash
# GiveCalc format_fig function
cat givecalc/ui/visualization.py

# Shows:
# - Roboto Serif font
# - White background
# - Logo placement
# - Margin configuration

Pattern to follow:

python
def format_fig(fig: go.Figure) -> go.Figure:
    """Format figure with PolicyEngine branding.

    This function is used across PolicyEngine projects to ensure
    consistent chart appearance.
    """
    # Font
    fig.update_layout(
        font=dict(family="Roboto Serif", color="black")
    )

    # Background
    fig.update_layout(
        template="plotly_white",
        plot_bgcolor="white"
    )

    # Size
    fig.update_layout(height=600, width=800)

    # Margins (room for logo)
    fig.update_layout(
        margin=dict(l=50, r=100, t=50, b=120, pad=4)
    )

    # Logo
    fig.add_layout_image(
        dict(
            source="https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/teal.png",
            xref="paper",
            yref="paper",
            x=1.0,
            y=-0.10,
            sizex=0.10,
            sizey=0.10,
            xanchor="right",
            yanchor="bottom"
        )
    )

    # Clean modebar
    fig.update_layout(
        modebar=dict(
            bgcolor="rgba(0,0,0,0)",
            color="rgba(0,0,0,0)"
        )
    )

    return fig

Streamlit Theme Configuration

Standard .streamlit/config.toml:

toml
[theme]
base = "light"
primaryColor = "#39C6C0"              # Teal accent
backgroundColor = "#FFFFFF"            # White
secondaryBackgroundColor = "#F7FDFC"  # Teal light
textColor = "#616161"                  # Dark gray
font = "sans serif"

[client]
toolbarMode = "minimal"
showErrorDetails = true

Usage:

bash
# Create .streamlit directory in your project
mkdir .streamlit

# Copy configuration
cat > .streamlit/config.toml << 'EOF'
[theme]
base = "light"
primaryColor = "#39C6C0"
backgroundColor = "#FFFFFF"
secondaryBackgroundColor = "#F7FDFC"
textColor = "#616161"
font = "sans serif"
EOF

Current examples:

bash
cat givecalc/.streamlit/config.toml

Design Patterns by Project Type

Streamlit Calculators (GiveCalc, SALT Calculator, etc.)

Required branding:

  1. ✅ .streamlit/config.toml with PolicyEngine theme
  2. ✅ Charts use format_fig() function with logo
  3. ✅ Teal accent color for interactive elements
  4. ✅ Roboto Serif for charts

Example:

python
import streamlit as st
import plotly.graph_objects as go

# Constants
TEAL_ACCENT = "#39C6C0"

# Streamlit UI
st.title("Calculator Name")  # Uses theme colors automatically
st.button("Calculate", type="primary")  # Teal accent from theme

# Charts
fig = go.Figure()
fig.add_trace(go.Scatter(x=x, y=y, line=dict(color=TEAL_ACCENT)))
fig = format_fig(fig)  # Add branding
st.plotly_chart(fig)

Jupyter Notebooks / Analysis Scripts

Required branding:

  1. ✅ Charts use format_fig() with logo
  2. ✅ PolicyEngine color palette
  3. ✅ Roboto Serif font

Example:

python
import plotly.graph_objects as go

TEAL_ACCENT = "#39C6C0"
BLUE_PRIMARY = "#2C6496"

fig = go.Figure()
fig.add_trace(go.Scatter(
    x=data.income,
    y=data.tax_change,
    line=dict(color=TEAL_ACCENT, width=3)
))

fig.update_layout(
    font=dict(family="Roboto Serif", size=14),
    title="Tax impact by income",
    xaxis_title="Income",
    yaxis_title="Tax change ($)",
    plot_bgcolor="white"
)

# Add logo
fig.add_layout_image(...)

React App Components

Color usage:

javascript
import colors from "style/colors";

// Interactive elements
<Button style={{ backgroundColor: colors.TEAL_ACCENT }}>
  Click me
</Button>

// Links
<a style={{ color: colors.BLUE }}>Learn more</a>

// Text
<p style={{ color: colors.DARK_GRAY }}>Description</p>

Current colors:

bash
cat policyengine-app/src/style/colors.js

Visual Guidelines

Chart Design Principles

  1. Minimal decoration - Let data speak
  2. White backgrounds - Clean, print-friendly
  3. Clear axis labels - Always include units
  4. Formatted numbers - Currency ($), percentages (%), etc.
  5. Logo inclusion - Bottom right, never intrusive
  6. Consistent sizing - 800x600 standard
  7. Roboto Serif - Professional, readable font

Color Usage Rules

Primary actions:

  • Use TEAL_ACCENT (#39C6C0)
  • Buttons, highlights, current selection

Chart lines:

  • Primary data: TEAL_ACCENT or BLUE_PRIMARY
  • Secondary data: BLUE_LIGHT or GRAY
  • Negative values: DARK_RED (#b50d0d)

Backgrounds:

  • Main: WHITE (#FFFFFF)
  • Secondary: TEAL_LIGHT (#F7FDFC) or BLUE_98 (#F7FAFD)
  • Plot area: WHITE

Text:

  • Primary: BLACK (#000000)
  • Secondary: DARK_GRAY (#616161)
  • Muted: GRAY (#808080)

Accessibility

Color contrast requirements:

  • Text on background: 4.5:1 minimum (WCAG AA)
  • DARK_GRAY on WHITE: ✅ Passes
  • TEAL_ACCENT on WHITE: ✅ Passes for large text
  • Use sufficient line weights for visibility

Don't rely on color alone:

  • Use patterns or labels for different data series
  • Ensure charts work in grayscale

Common Branding Tasks

Task 1: Create Branded Plotly Chart

  1. Define colors:

    python
    TEAL_ACCENT = "#39C6C0"
    BLUE_PRIMARY = "#2C6496"
    
  2. Create chart:

    python
    fig = go.Figure()
    fig.add_trace(go.Scatter(x=x, y=y, line=dict(color=TEAL_ACCENT)))
    
  3. Apply branding:

    python
    fig = format_fig(fig)  # See implementation above
    

Task 2: Setup Streamlit Branding

  1. Create config directory:

    bash
    mkdir .streamlit
    
  2. Copy theme config:

    bash
    cat givecalc/.streamlit/config.toml > .streamlit/config.toml
    
  3. Verify in app:

    python
    import streamlit as st
    
    st.button("Test", type="primary")  # Should be teal
    

Task 3: Brand Consistency Check

Checklist:

  • Charts use Roboto Serif font
  • Primary color is TEAL_ACCENT (#39C6C0)
  • Secondary color is BLUE_PRIMARY (#2C6496)
  • White backgrounds
  • Logo in charts (bottom right)
  • Currency formatted with $ and commas
  • Percentages formatted with %
  • Streamlit config.toml uses PolicyEngine theme

Reference Implementations

Excellent Examples

Streamlit calculators:

bash
# GiveCalc - Complete example
cat givecalc/ui/visualization.py
cat givecalc/.streamlit/config.toml

# Other calculators
ls salt-amt-calculator/
ls ctc-calculator/

Blog post charts:

bash
# Analysis with branded charts
cat policyengine-app/src/posts/articles/harris-eitc.md
cat policyengine-app/src/posts/articles/montana-tax-cuts-2026.md

React app components:

bash
# Charts in app
cat policyengine-app/src/pages/policy/output/DistributionalImpact.jsx

Don't Use These

❌ Wrong colors:

python
# Don't use random colors
color = "#FF5733"
color = "red"
color = "green"

❌ Wrong fonts:

python
# Don't use other fonts for charts
font = dict(family="Arial")
font = dict(family="Times New Roman")

❌ Missing logo:

python
# Don't skip the logo in charts for publication
# All published charts should include PolicyEngine logo

Assets and Resources

Logo Files

In policyengine-app repository:

bash
policyengine-app/src/images/logos/policyengine/
├── blue.png      # Primary logo (light backgrounds)
├── white.png     # Logo for dark backgrounds
├── blue.svg      # Scalable blue logo
├── white.svg     # Scalable white logo
├── banners/      # Banner variations
└── profile/      # Profile/avatar versions

Raw URLs for direct use:

python
# Use these URLs in code
LOGO_URL = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/teal.png"

Font Files

Roboto (charts):

Loading:

html
<link href="https://fonts.googleapis.com/css2?family=Roboto+Serif:wght@300;400;500;700&display=swap" rel="stylesheet">

Color Reference Files

JavaScript (React app):

bash
cat policyengine-app/src/style/colors.js

Python (calculators, analysis):

python
# Define in constants.py or at top of file
TEAL_ACCENT = "#39C6C0"
BLUE_PRIMARY = "#2C6496"
DARK_GRAY = "#616161"
WHITE = "#FFFFFF"

Brand Evolution

Current identity (2025):

  • Teal primary (#39C6C0)
  • Blue secondary (#2C6496)
  • Roboto Serif for charts
  • Minimal, data-focused design

If brand evolves:

  • Colors defined in policyengine-app/src/style/colors.js are source of truth
  • Update this skill to point to current definitions
  • Never hardcode - always reference colors.js

Quick Reference

Color Codes

Color Hex Usage
Teal Accent #39C6C0 Primary interactive elements
Blue Primary #2C6496 Secondary, links, charts
Dark Gray #616161 Body text
White #FFFFFF Backgrounds
Teal Light #F7FDFC Secondary backgrounds
Dark Red #b50d0d Negative values, errors

Font Families

Context Font
Charts Roboto Serif
Web app System sans-serif
Streamlit Default sans-serif
Code blocks Monospace

Logo URLs

Background Format URL
Light PNG https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/teal.png
Light SVG https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/teal.svg
Dark PNG https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/white.png
Dark SVG https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/white.svg

Related Skills

  • policyengine-app-skill - React component styling
  • policyengine-analysis-skill - Chart creation patterns
  • policyengine-writing-skill - Content style (complements visual style)

Resources

Brand assets: PolicyEngine/policyengine-app/src/images/ Color definitions: PolicyEngine/policyengine-app/src/style/colors.js Examples: givecalc, salt-amt-calculator, crfb-tob-impacts

Didn't find tool you were looking for?

Be as detailed as possible for better results