The Illuminators' Workshop
Git for design, LFS, SVG and design tokens
In the west wing of the Citadel, bathed in the soft light of stained glass windows, lies the Illuminators' Workshop. Here, the realm's artists create the ornaments, illustrations and interfaces that bring scrolls to life. Their digital brushes produce visual works that cannot be reduced to text - and that's precisely the challenge.
The Master Illuminator observes you with curiosity. "You know how to version code, Versioner. But do you know how to version art? A 200 MB Photoshop file? A complete design system? Color tokens shared across ten applications? Welcome to our world. It's beautiful, but it doesn't follow your usual rules."
The problem with graphic assets
In a software project, designers produce files very different from source code:
- Large source files: .psd (Photoshop), .ai (Illustrator), .sketch, .fig (Figma) - from 10 MB to 500 MB each
- Binary formats: Git cannot compute textual diffs
- Frequent modifications: a designer may save 50 times a day
- Numerous variants: icon-v1.psd, icon-v2.psd, icon-v2-final.psd, icon-v2-final-REAL.psd...
Without an adapted solution, a repository with graphic assets quickly becomes unmanageable:
# A repo without Git LFS, after 6 months of design:
du -sh .git/
# 4.7 GB .git/
# Every version of every PSD is stored in full in the history.
# Cloning takes 45 minutes.
# Nobody is happy. Git LFS for design
You may have already encountered Git LFS (Large File Storage) in other quests. Here's a quick refresher and the patterns specific to design.
Principle reminder
Git LFS replaces large files in your repository with small "pointer" files. The real files are stored on a separate LFS server. Result: the repository stays lightweight, clones are fast.
# Installation (once per machine)
git lfs install
# In your project, track design files
git lfs track "*.psd"
git lfs track "*.ai"
git lfs track "*.sketch"
git lfs track "*.fig"
git lfs track "*.xd"
git lfs track "*.tiff"
git lfs track "*.raw"
# This modifies .gitattributes
cat .gitattributes
# *.psd filter=lfs diff=lfs merge=lfs -text
# *.ai filter=lfs diff=lfs merge=lfs -text
# *.sketch filter=lfs diff=lfs merge=lfs -text
# *.fig filter=lfs diff=lfs merge=lfs -text
# *.xd filter=lfs diff=lfs merge=lfs -text
# *.tiff filter=lfs diff=lfs merge=lfs -text
# *.raw filter=lfs diff=lfs merge=lfs -text Complete LFS patterns for design
# .gitattributes - recommended patterns for a design repo
# Design source files
*.psd filter=lfs diff=lfs merge=lfs -text
*.psb filter=lfs diff=lfs merge=lfs -text
*.ai filter=lfs diff=lfs merge=lfs -text
*.sketch filter=lfs diff=lfs merge=lfs -text
*.fig filter=lfs diff=lfs merge=lfs -text
*.xd filter=lfs diff=lfs merge=lfs -text
# High resolution images
*.tiff filter=lfs diff=lfs merge=lfs -text
*.tif filter=lfs diff=lfs merge=lfs -text
*.raw filter=lfs diff=lfs merge=lfs -text
*.bmp filter=lfs diff=lfs merge=lfs -text
# Exported images (optional - some prefer not to LFS PNGs)
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
# Fonts
*.otf filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.woff filter=lfs diff=lfs merge=lfs -text
*.woff2 filter=lfs diff=lfs merge=lfs -text
# Videos and animations
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.mov filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text Tip: For PNG and JPG files, the decision depends on your situation. If they're small icons (a few KB), no need for LFS. If they're high-resolution mockups (several MB), LFS is recommended. The rule: LFS for any file over 500 KB.
Visual diff for images
When a designer modifies a mockup, how do you know what changed? Visual diff tools compare two versions of an image and show the differences.
git-diff-image
git-diff-image is an open source tool that integrates directly into git diff to display visual differences between two versions of an image:
# Installation
git clone https://github.com/ewanmellor/git-diff-image.git
cd git-diff-image
./install.sh
# After installation, git diff automatically shows
# visual differences for images
git diff HEAD~1 -- assets/hero-banner.png
# Opens a window with both versions side by side
# and an overlay view showing the modified pixels Beyond Compare and Kaleidoscope
For professional use, tools like Beyond Compare (cross-platform) or Kaleidoscope (macOS) offer advanced features:
- Side by side mode: both versions next to each other
- Overlay mode: one image on top of the other with a slider
- Difference mode: only differing pixels are displayed
- Synchronized zoom: zoom into a detail in both versions
# Configure Beyond Compare as diff tool for images
git config --global diff.tool bc4
git config --global difftool.bc4.cmd '"C:/Program Files/Beyond Compare 4/BComp.exe" "$LOCAL" "$REMOTE"'
# Usage
git difftool HEAD~1 -- assets/hero-banner.png # PowerShell - identical configuration
git config --global diff.tool bc4
git config --global difftool.bc4.cmd '"C:\Program Files\Beyond Compare 4\BComp.exe" "$LOCAL" "$REMOTE"' SVG in Git - the dream format
The SVG (Scalable Vector Graphics) format is a special and wonderful case for versioning: it's XML, therefore text. Git can compute diffs, and you can see exactly what changed.
# An SVG file is text:
cat icon-home.svg
# <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
# <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
# </svg>
# Git diff works perfectly:
git diff icon-home.svg
# - <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
# + <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"
# + fill="#333333"/> SVG advantages in Git
- Readable diffs: you see exactly which attribute changed
- Lightweight files: no need for Git LFS
- Scalable: no quality loss, perfect for icons and illustrations
- Mergeable: in theory, Git can merge SVG modifications (with caution)
Optimization with SVGO
SVGO (SVG Optimizer) cleans and optimizes SVG files exported from design software. SVGs exported from Illustrator or Figma often contain unnecessary code:
# Installation
npm install -g svgo
# Optimize an SVG file
svgo icon-home.svg
# icon-home.svg: 1.24 KB -> 0.45 KB (63.7% reduction)
# Optimize an entire folder
svgo -f assets/icons/ -o assets/icons-optimized/
# Custom configuration (.svgo.config.js)
cat > .svgo.config.js << 'EOF'
module.exports = {
plugins: [
'preset-default',
'removeDimensions', // Use viewBox instead of width/height
{
name: 'removeAttrs',
params: {
attrs: ['data-name'] // Remove Illustrator attributes
}
}
]
}
EOF
# Tip: add SVGO as a pre-commit hook
# to automatically optimize SVGs before each commit Tip: Prefer SVG for anything that can use it: icons, logos, simple illustrations, graphics. Reserve bitmap formats (PNG, JPG) for photos and complex images that don't vectorize well.
Designers + developers workflow
The biggest challenge isn't technical - it's human. How do you get designers (who think in pixels) and developers (who think in code) to work together on the same repository?
Who touches what?
# Clear structure with separation of responsibilities
my-project/
βββ src/ # Developers: application code
βββ assets/
β βββ raw/ # Designers: source files (PSD, AI, Sketch)
β β βββ hero-banner.psd
β βββ exported/ # Designers: final exports
β β βββ hero-banner.png
β β βββ hero-banner@2x.png
β β βββ icons/
β β βββ home.svg
β β βββ search.svg
β βββ fonts/ # Shared: fonts used
βββ tokens/ # Shared: design tokens (JSON/YAML)
βββ docs/
βββ design/ # Designers: visual documentation Naming conventions
Strict conventions prevent chaos:
- Lowercase with hyphens:
hero-banner.png, notHero Banner.pngorHeroBanner.png - No spaces: spaces in filenames cause problems everywhere
- Size suffixes:
icon@1x.png,icon@2x.png,icon@3x.png - No "final" in the name: Git manages versions, not the filename
Anti-pattern: logo-v3-final-FINAL-fixed-2.psd. With Git, the latest version is always logo.psd on the main branch. Period.
File locking for binary assets
Two designers can't modify the same PSD file simultaneously - there's no possible merge on a binary. The solution: Git LFS file locking.
# Configure locking in .gitattributes
# *.psd filter=lfs diff=lfs merge=lfs -text lockable
# *.ai filter=lfs diff=lfs merge=lfs -text lockable
# Before modifying a file, lock it
git lfs lock assets/raw/hero-banner.psd
# Locked assets/raw/hero-banner.psd
# See locked files
git lfs locks
# ID Path Owner Locked At
# 1234 assets/raw/hero-banner.psd alice 2026-03-09T14:30:00Z
# After committing, unlock
git lfs unlock assets/raw/hero-banner.psd
# If a colleague tries to modify a locked file:
git push
# Forbidden: assets/raw/hero-banner.psd is locked by alice Figma integration and design tokens
Figma is today's dominant design tool. Its files are stored in the cloud (not in Git), but you can create a bridge between Figma and Git through design tokens.
What is a design token?
A design token is an abstract design value: a color, a font size, a spacing, a border radius. Instead of writing #3B82F6 everywhere, you write color.primary.500.
# tokens/colors.json - Design tokens in W3C format
{
"color": {
"primary": {
"50": { "$value": "#EFF6FF", "$type": "color" },
"100": { "$value": "#DBEAFE", "$type": "color" },
"500": { "$value": "#3B82F6", "$type": "color" },
"700": { "$value": "#1D4ED8", "$type": "color" },
"900": { "$value": "#1E3A5F", "$type": "color" }
},
"neutral": {
"0": { "$value": "#FFFFFF", "$type": "color" },
"100": { "$value": "#F3F4F6", "$type": "color" },
"900": { "$value": "#111827", "$type": "color" }
}
},
"spacing": {
"xs": { "$value": "4px", "$type": "dimension" },
"sm": { "$value": "8px", "$type": "dimension" },
"md": { "$value": "16px", "$type": "dimension" },
"lg": { "$value": "24px", "$type": "dimension" },
"xl": { "$value": "32px", "$type": "dimension" }
},
"font": {
"family": {
"heading": { "$value": "Inter, sans-serif", "$type": "fontFamily" },
"body": { "$value": "Inter, sans-serif", "$type": "fontFamily" },
"mono": { "$value": "JetBrains Mono, monospace", "$type": "fontFamily" }
},
"size": {
"sm": { "$value": "14px", "$type": "dimension" },
"md": { "$value": "16px", "$type": "dimension" },
"lg": { "$value": "20px", "$type": "dimension" },
"xl": { "$value": "24px", "$type": "dimension" }
}
},
"border": {
"radius": {
"sm": { "$value": "4px", "$type": "dimension" },
"md": { "$value": "8px", "$type": "dimension" },
"full": { "$value": "9999px", "$type": "dimension" }
}
}
} Export from Figma to Git
Several Figma plugins allow you to export tokens directly in a usable format:
- Tokens Studio for Figma: the most popular. Synchronizes tokens between Figma and a Git repository (GitHub, GitLab). Designers modify in Figma, tokens are automatically updated in the repo.
- Figma Tokens: manual or automated export to JSON
- Figma API: custom scripts to extract styles
# Workflow with Tokens Studio
# 1. The designer modifies a color in Figma
# 2. Tokens Studio detects the change
# 3. Tokens Studio creates a Pull Request on GitHub
# with the updated tokens.json file
# 4. The developer reviews and merges the PR
# 5. CI/CD generates the CSS/SCSS/JS variables Style Dictionary - transforming tokens
Style Dictionary (by Amazon) transforms design tokens JSON into variables usable on any platform:
# Installation
npm install -g style-dictionary
# Configuration (config.json)
cat > config.json << 'EOF'
{
"source": ["tokens/**/*.json"],
"platforms": {
"css": {
"transformGroup": "css",
"buildPath": "build/css/",
"files": [{
"destination": "variables.css",
"format": "css/variables"
}]
},
"scss": {
"transformGroup": "scss",
"buildPath": "build/scss/",
"files": [{
"destination": "_variables.scss",
"format": "scss/variables"
}]
},
"js": {
"transformGroup": "js",
"buildPath": "build/js/",
"files": [{
"destination": "tokens.js",
"format": "javascript/es6"
}]
}
}
}
EOF
# Generate variables for all platforms
style-dictionary build
# CSS result:
cat build/css/variables.css
# :root {
# --color-primary-50: #EFF6FF;
# --color-primary-500: #3B82F6;
# --spacing-sm: 8px;
# --spacing-md: 16px;
# --font-size-md: 16px;
# --border-radius-md: 8px;
# }
# SCSS result:
cat build/scss/_variables.scss
# $color-primary-50: #EFF6FF;
# $color-primary-500: #3B82F6;
# $spacing-sm: 8px; The W3C Design Tokens format
The W3C Design Tokens Community Group is working on a standard for design tokens. The format we used above ($value, $type) is the emerging W3C format. It aims to become the universal standard.
Format structure
# W3C Design Tokens format
{
"group-name": {
"token-name": {
"$value": "...", # The token value
"$type": "color", # The type (color, dimension, fontFamily, etc.)
"$description": "..." # Optional description
}
}
}
# Supported types:
# color - colors (#hex, rgb, hsl)
# dimension - sizes (px, rem, em)
# fontFamily - font families
# fontWeight - weights (400, 700, bold)
# duration - animation durations (200ms)
# cubicBezier - animation curves
# shadow - box shadows
# gradient - gradients The advantage of using the W3C format: all tools are converging toward this standard. Tokens Studio, Style Dictionary, and many other tools already support it.
Organizing a design repo
my-design-system/
βββ assets/
β βββ raw/ # Design sources (PSD, AI, Sketch)
β β βββ illustrations/ # High resolution illustrations
β β βββ photography/ # Reference photos
β β βββ mockups/ # Screen mockups
β βββ exported/ # Optimized exports ready to use
β βββ icons/ # Optimized SVG icons
β β βββ home.svg
β β βββ search.svg
β β βββ user.svg
β βββ logos/ # Logos in various formats
β β βββ logo-full.svg
β β βββ logo-icon.svg
β β βββ logo-full.png
β βββ images/ # Product images
βββ tokens/ # Design tokens (source of truth)
β βββ colors.json
β βββ spacing.json
β βββ typography.json
β βββ shadows.json
βββ build/ # Generated files (in .gitignore)
β βββ css/variables.css
β βββ scss/_variables.scss
β βββ js/tokens.js
βββ components/ # Documented components
β βββ button/
β β βββ button.md # Component documentation
β β βββ button-preview.png # Visual preview
β βββ card/
β βββ card.md
β βββ card-preview.png
βββ docs/ # Design system documentation
β βββ getting-started.md
β βββ contribution-guide.md
βββ .gitattributes # LFS patterns
βββ .gitignore
βββ config.json # Style Dictionary config
βββ package.json Tip: The build/ folder should be in .gitignore - it's automatically generated by Style Dictionary. Only the tokens/ files (the source of truth) are versioned.
Practical exercise - Design repo with LFS and tokens
Create a repository for a fictional design system (the Citadel's "Visual Grimoire") with Git LFS for assets and versioned design tokens:
- Create the recommended structure
- Configure Git LFS for design files
- Add design tokens in JSON (W3C format)
- Create SVG icons and optimize them
- Simulate a color change and observe the diff
Step 1 - Create the structure
# Create the repository
mkdir visual-grimoire
cd visual-grimoire
git init -b main
# Create the directory tree
mkdir -p assets/raw/illustrations
mkdir -p assets/raw/mockups
mkdir -p assets/exported/icons
mkdir -p assets/exported/logos
mkdir -p assets/exported/images
mkdir -p tokens
mkdir -p components/button
mkdir -p components/card
mkdir -p docs Step 2 - Configure Git LFS
# Initialize Git LFS
git lfs install
# Track design files
git lfs track "*.psd"
git lfs track "*.ai"
git lfs track "*.sketch"
git lfs track "*.tiff"
git lfs track "*.raw"
git lfs track "assets/exported/images/*.png"
git lfs track "assets/exported/images/*.jpg"
# Create the .gitignore
cat > .gitignore << 'EOF'
# Generated files
build/
node_modules/
# OS
.DS_Store
Thumbs.db
Desktop.ini
# Editors
*.swp
*.swo
*~
EOF
# First commit
git add .gitattributes .gitignore
git commit -m "Initial configuration: Git LFS + .gitignore" Step 3 - Add design tokens
# Create color tokens
cat > tokens/colors.json << 'EOF'
{
"color": {
"primary": {
"500": { "$value": "#7C3AED", "$type": "color", "$description": "Main Grimoire violet" },
"700": { "$value": "#5B21B6", "$type": "color", "$description": "Dark violet" }
},
"accent": {
"500": { "$value": "#F59E0B", "$type": "color", "$description": "Illumination gold" }
},
"neutral": {
"0": { "$value": "#FFFFFF", "$type": "color" },
"900": { "$value": "#1F2937", "$type": "color" }
}
}
}
EOF
# Create spacing tokens
cat > tokens/spacing.json << 'EOF'
{
"spacing": {
"xs": { "$value": "4px", "$type": "dimension" },
"sm": { "$value": "8px", "$type": "dimension" },
"md": { "$value": "16px", "$type": "dimension" },
"lg": { "$value": "24px", "$type": "dimension" },
"xl": { "$value": "32px", "$type": "dimension" }
}
}
EOF
git add tokens/
git commit -m "Add color and spacing design tokens" Step 4 - Create SVG icons
# Create a simple SVG icon
cat > assets/exported/icons/shield.svg << 'EOF'
EOF
cat > assets/exported/icons/scroll.svg << 'EOF'
EOF
git add assets/exported/icons/
git commit -m "Add shield and scroll SVG icons" Step 5 - Simulate a color change
# The designer decides to change the main violet
sed -i 's/#7C3AED/#8B5CF6/' tokens/colors.json
sed -i 's/#5B21B6/#6D28D9/' tokens/colors.json
# Observe the diff - perfectly readable!
git diff tokens/colors.json
# - "500": { "$value": "#7C3AED", "$type": "color", ...
# + "500": { "$value": "#8B5CF6", "$type": "color", ...
# - "700": { "$value": "#5B21B6", "$type": "color", ...
# + "700": { "$value": "#6D28D9", "$type": "color", ...
# Commit the change
git add tokens/colors.json
git commit -m "Update primary color: lighter violet"
# The history clearly shows the evolution
git log --oneline
# abc1234 Update primary color: lighter violet
# def5678 Add shield and scroll SVG icons
# ghi9012 Add color and spacing design tokens
# jkl3456 Initial configuration: Git LFS + .gitignore Concept summary
| Concept | Description |
|---|---|
| Git LFS for design | Track PSD, AI, Sketch and other large binaries |
| File locking | Lock a binary file to prevent conflicts |
| Visual diff | Tools for visually comparing two versions of an image |
| SVG | Text-based vector format - perfect diffs in Git |
| SVGO | SVG optimizer, removes unnecessary code |
| Design tokens | Abstract design values (colors, sizes) in JSON |
| W3C format | Emerging standard for design tokens ($value, $type) |
| Tokens Studio | Figma plugin for synchronizing tokens with Git |
| Style Dictionary | Tool for transforming JSON tokens into CSS/SCSS/JS |
| Naming conventions | Lowercase, hyphens, no spaces, no "final" |
The Master Illuminator examines your repository with an approving smile. "You've understood the essentials: design and code don't live in separate worlds. They share the same source of truth - tokens. They live in the same repository - with LFS for large files. They speak the same language - that of Git."
"Too many projects fail because designers work in their corner and developers in theirs. Colors don't match, spacing is inconsistent, icons have three different names. With what you know now, you can build a bridge between these two worlds."
She hands you a brush whose handle is engraved with Git tree branches. "This is the symbol of the Versioning Illuminators. You now know that art, like code, deserves a history. That every pixel changed should be documented. That every color should have its source of truth. Go forth, and may your designs be as clean as your commits."