Initial commit
This commit is contained in:
880
skills/policyengine-design-skill/SKILL.md
Normal file
880
skills/policyengine-design-skill/SKILL.md
Normal file
@@ -0,0 +1,880 @@
|
||||
---
|
||||
name: policyengine-design
|
||||
description: PolicyEngine visual identity - colors, fonts, logos, and branding for web apps, calculators, charts, and research
|
||||
---
|
||||
|
||||
# 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/master/src/images/logos/policyengine/blue.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/...
|
||||
```
|
||||
|
||||
#### 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:**
|
||||
|
||||
```python
|
||||
# Blue logo (for light backgrounds)
|
||||
LOGO_BLUE = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png"
|
||||
|
||||
# White logo (for dark backgrounds)
|
||||
LOGO_WHITE = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/white.png"
|
||||
|
||||
# SVG versions (scalable)
|
||||
LOGO_BLUE_SVG = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.svg"
|
||||
LOGO_WHITE_SVG = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/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/src/images/logos/policyengine/
|
||||
```
|
||||
|
||||
### 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/master/src/images/logos/policyengine/blue.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/master/src/images/logos/policyengine/blue.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/master/src/images/logos/policyengine/blue.png"
|
||||
```
|
||||
|
||||
### Font Files
|
||||
|
||||
**Roboto (charts):**
|
||||
- Google Fonts: https://fonts.google.com/specimen/Roboto
|
||||
- Family: Roboto Serif
|
||||
- Weights: 300 (light), 400 (regular), 500 (medium), 700 (bold)
|
||||
|
||||
**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/master/src/images/logos/policyengine/blue.png |
|
||||
| Light | SVG | https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.svg |
|
||||
| Dark | PNG | https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/white.png |
|
||||
| Dark | SVG | https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/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
|
||||
Reference in New Issue
Block a user