Design System · April 2026
Mullin360 Brand Identity.
This document is the source of truth for tokens, components, and patterns used on mullinv1.netlify.app. Every value shown is extracted directly from index.html and its sibling pages — so what you see here is what renders in production. Each section is structured to map cleanly onto Figma frames: one page per foundation, one frame per component, and one master file for patterns.
Foundations · 01
Colors
Two intentional families: a deep green brand spectrum and a warm-leaning neutral. The palette is small by design, but opacity variants are overused — see the Gaps section.
Brand · Green
Neutrals
Common overlays / tints used in the site
| Value | Where | Note |
|---|---|---|
rgba(255,255,255,.78) | Nav links default | — |
rgba(255,255,255,.62) | Footer links | Below 4.5:1 on charcoal |
rgba(245,240,232,.55) | Card meta text | cream-leaning white; off-palette |
rgba(14,97,51,.35) | Button hover shadow | — |
rgba(14,97,51,.08) | Field focus ring | — |
rgba(220,233,225,.25 / .4) | Status-badge borders | Two similar variants; consolidate |
--cream (#0c1a11), --terra (alias of --green), and --terra-light
(alias of --green-soft) are declared but never referenced. Delete from :root
or repurpose as canonical names — aliases fragment the token graph.
Foundations · 02
Typography
Two families: Cormorant Garamond for expressive display, DM Sans for the rest. The display voice leans 300-weight and uses italicized ems in green-pale for emphasis. Body sizing is fluid via clamp() — good in practice, but no named scale means every headline picks its own clamp range.
Font families
Display specimens (Cormorant)
others hesitate.
UI specimens (DM Sans)
Letter-spacing cheat sheet
| Token candidate | Value | Used in |
|---|---|---|
| --tracking-hero | .32em | Hero eyebrow |
| --tracking-label | .25em | Section labels, eyebrows |
| --tracking-footer | .2em | Footer heads, status, info labels |
| --tracking-form | .18em | Form labels |
| --tracking-nav | .16em | Nav links, filter buttons |
| --tracking-btn | .14em | Primary buttons |
| --tracking-btn-sm | .12em | Small buttons, CTA links |
Consolidate Seven adjacent values is too many. Recommend collapsing to 3: .12em (buttons), .18em (labels), .28em (eyebrows). | ||
Foundations · 03
Spacing
The site uses 17 distinct spacing values without a declared scale. Most values fall on or near a 4px
grid, but there's meaningful drift — values like .45rem and .55rem exist for
one-off nudges. Below is the observed spectrum and the recommended reduced scale.
Observed spacing (in rem, 16px base)
4px
8px
12px
16px
20px
24px
32px
40px
48px
64px
80px
96px
Drift
Non-conforming values seen in production: .2rem, .3rem, .4rem,
.45rem, .55rem, .6rem, .8rem, .85rem,
.9rem, 1.1rem, 1.2rem, 1.75rem, 3.5rem,
4.5rem. Recommend snapping to the 12-step scale above.
Foundations · 04
Border radius
Three effective values. Sharp corners are the default — radii are used sparingly for "click" affordance on primary buttons and 100% for dots/avatars.
Foundations · 05
Shadows / elevation
Shadows are used as motion accents (on hover / lift) rather than for static elevation. No card shadow tokens exist — cards use borders instead.
Foundations · 06
Borders
| Width | Color | Used for |
|---|---|---|
| 1px solid | var(--line) #D5DED8 | Form fields, info blocks, cards, dividers |
| 1px solid | var(--green) | Field focus state |
| 1px solid | rgba(245,240,232,.08) | Footer bottom |
| 2px solid | var(--green) | Blockquote left border |
| 2px solid | rgba(255,255,255,.75) | Secondary button (.btn-sage on dark) |
| 2px solid | rgba(14,97,51,.18) | Avatar placeholder |
Foundations · 07
Motion
Fourteen distinct duration values is too many. Recommend collapsing to a five-step scale (100 / 200 / 350 / 600 / 1000ms) plus two easings.
Durations observed
| Duration | Used in | Recommended |
|---|---|---|
| .15s | Button active state | 100ms |
| .2s / .3s | Hover colors, filter active | 200ms |
| .35s / .4s | Button transforms, nav scroll | 350ms |
| .5s / .6s / .7s | Image zoom on hover | 600ms |
| .8s–1.2s | Page reveal / fade-up | 1000ms |
| 1.8s / 2.2s | Scroll arrow bounce (ambient) | 1800ms (keep) |
Easings
| Name | Value | Used for |
|---|---|---|
| --ease-out-site | cubic-bezier(.22, 1, .36, 1) | Reveals, card image zooms |
| --ease-out-btn | cubic-bezier(.23, 1, .32, 1) | Button transforms on hover |
| --ease-std | ease / ease-in-out | Hover color swaps (consolidate) |
Good prefers-reduced-motion: reduce is respected across all keyframe animations.
Foundations · 08
Breakpoints
| Name | Query | Drop-ins |
|---|---|---|
| desktop (default) | — | 3-col grids, 5rem section padding |
| tablet | (max-width: 1024px) | 2-col grids, 2rem nav padding, split sections stack |
| mobile | (max-width: 640px) | 1-col grids, nav links hidden, hero type shrinks |
Gap No tablet-portrait (768px) breakpoint — layouts either commit desktop or mobile behavior in that range.
Components · 02
Buttons
Four variants in production. Inconsistent radius between .btn-terra (6px) and .btn-submit (0) is the top fix.
On light surface
On dark surface
| Variant | Padding | Radius | Size | Hover |
|---|---|---|---|---|
| .btn-terra | 1.25rem 3rem | 6px | .85rem · .14em | bg → green-deep · ty(-6px) · shadow |
| .btn-sage | 1.25rem 3rem | 6px | .85rem · .14em | bg tint · border → green · ty(-4px) |
| .btn-submit | 1rem 2.5rem | 0 | .72rem · .12em | bg → green-deep · ty(-1px) |
| .btn-cream | 1rem 2.5rem | 0 | .72rem · .12em | bg → green-pale |
| Disabled | Not defined | |||
| Focus | Not defined | |||
| Loading | Not defined | |||
Components · 03
Form fields
Label + input stacked vertically. Focus ring is good; error and success states don't exist yet.
| Default | 1px solid #D5DED8 · .8rem 1rem padding |
| Focus | border → --green · shadow 0 0 0 3px rgba(14,97,51,.08) |
| Error | Not defined — add: border → #a3262c · inline message .8rem |
| Disabled | Not defined — add: bg → --mist · color → --stone-light |
| Placeholder | color --stone-light · ~4.1:1 contrast, borderline |
Components · 04
Filter chips
Components · 05
Status badges
Components · 06
Info blocks
Components · 07
Venture links
Components · 08
Project cards
Hover-only affordance The arrow icon and status badge only appear on hover. Consider making them visible on touch devices.
Components · 09
Team cards
Patterns · 01
Hero
Patterns · 02
Split section
OUR VISION
Long-horizon investment in the Sun Corridor.
We build neighborhoods, not parcels. Every project is shaped by climate, community, and what's already there.
Our portfolio spans automotive, hospitality, and mixed-use — always with a 20-year view.
Patterns · 03
Stats strip
Patterns · 04
Pillars
OUR APPROACH
How we build, summarized.
Climate-first design
Desert-tuned massing, shade, and thermal envelope decisions at land-entitlement stage.
Community alignment
City conversations start before the survey. Projects are shaped by what the place already tells us.
Long-hold economics
We develop to own, not to flip. That changes every material decision.
Audit · 01
Maturity score · 62/100
| Category | Score | Notes |
|---|---|---|
| Color tokens | 8 / 10 | Well-structured; three unused aliases to remove |
| Typography tokens | 7 / 10 | Good families, no named size scale |
| Spacing tokens | 5 / 10 | 17 observed values, no declared scale |
| Radius tokens | 6 / 10 | 3 values, inconsistent between button variants |
| Shadow tokens | 6 / 10 | 3 in use, none named; no card elevation |
| Motion tokens | 5 / 10 | 14 durations — consolidate |
| Component completeness | 6 / 10 | Missing: focus, error, disabled, loading states |
| Accessibility | 5 / 10 | Contrast fails in footer; focus rings absent |
| Documentation | 4 / 10 | This doc closes the gap |
| Code ↔ design parity | 7 / 10 | Good — tokens in CSS variables |
Audit · 02
Gaps · hardcoded & inconsistent values
Hardcoded pixel values (should be tokens)
| Value | Where | Issue |
|---|---|---|
20px | Grid gaps across projects/team/gallery | Recommend --space-5 (20px) token |
34 / 30 / 26px | Nav logo height at 3 breakpoints | Extract to --nav-logo-h |
420 / 380 / 280 / 300px | Card image heights | Define --card-media-h tokens |
60px | JS scroll threshold | Expose as data-attribute or token |
.45rem / .55rem / .85rem | Various off-scale spacing | Snap to 4px scale |
Near-duplicates
| Items | Recommendation |
|---|---|
--charcoal #102218 vs --charcoal-soft #1A2E22 | Slight variation, rarely used deliberately. Consider dropping one. |
rgba(255,255,255,.78) vs .62 vs .55 vs .5 | Define an opacity scale: --opacity-primary .88, --opacity-muted .66, --opacity-subtle .4 |
Easings cubic-bezier(.22,1,.36,1) vs (.23,1,.32,1) | Nearly identical — collapse to one named easing |
--terra, --terra-light, --cream | Unused aliases — delete from :root |
Missing component states
| Component | Missing |
|---|---|
| Buttons | Focus ring, disabled, loading |
| Nav links | Focus ring |
| Project cards | Focus ring, keyboard-visible status/arrow |
| Form fields | Error, success, disabled |
| Filter chips | Focus ring |
Audit · 03
WCAG contrast spot-checks
| Pair | Foreground | Background | Ratio | Status |
|---|---|---|---|---|
| Body on white | --stone #5F6F66 | #FFFFFF | ~5.5:1 | AA |
| Labels on white | --stone-light #86958D | #FFFFFF | ~4.1:1 | Borderline large-text only |
| Nav links | rgba(255,255,255,.78) | #102218 (scrolled) | ~6.1:1 | AA |
| Footer links | rgba(255,255,255,.62) | #102218 | ~3.8:1 | Fail 4.5:1 |
| Footer copyright | rgba(255,255,255,.5) | #102218 | ~2.9:1 | Fail |
| Card meta | rgba(245,240,232,.55) | pcard overlay dark | ~3.5:1 | Large text only |
| Green-pale on green | #DCE9E1 | #0E6133 | ~2.1:1 | Decorative only — don't use for text |
| #f0c060 on dark | #f0c060 | rgba(0,40,32,.97) | ~7.2:1 | AA |
Audit · 04
Priority actions
- Lift footer link opacity from
.62→.78; copyright to.72. - Add
:focus-visiblering to buttons, nav links, cards, filter chips (2px--green-paleoutline, 2px offset). - Unify button radius — decide 0 or 6px and apply to all four variants.
- Publish a 12-step spacing scale and codemod off-scale values.
- Collapse 14 motion durations → 5 (100/200/350/600/1000ms).
- Consolidate letter-spacing values from 7 → 3.
- Define error / disabled / success states for form fields and buttons.
- Add a 768px tablet-portrait breakpoint.
- Extract image-height tokens for media cards.
- Remove unused aliases
--cream,--terra,--terra-light. - Add skip-to-content link on every page.
- Axe Core failures: should reach 0
- Unique spacing values: < 14
- Unique motion durations: < 6
- CSS custom properties with 0 references: 0
Delivery · Figma
How to drop this into Figma
Three realistic paths, in order of effort:
Install html.to.design in Figma (free tier covers single-file docs). Paste this document's URL (or drag the file) — it auto-generates a Figma page with real frames and text layers. Best one-click option.
I can spin up a design-tokens.json matching W3C DTCG format from the values in this doc. Load it into Tokens Studio for Figma. You'll get colors + typography styles + spacing variables wired to Figma variables in about 5 minutes.
Low-tech but reliable: take screenshots of each section, paste onto a Figma page for reference, and rebuild natively. Best if you want clean Figma components from day one.
Say the word ("generate tokens.json") and I'll produce the file. Likewise if you want a Figma