Summary
The brand is clear and distinctive. The way it is coded is not consistent.
The newer “neon” palette is recognisable and confident: deep purple, heliotrope, grass green, square corners and offset block shadows. Underneath it, though, the token layer has two competing colour namespaces, a semantic layer that collapses into a few hex values, conflicting redefinitions, and widespread hardcoding of type, spacing and motion values. Components are well chosen for a university site. Their states, variants and documentation are uneven.
!important declarationsThree token layers that overlap
The site's CSS loads three overlapping vocabularies, and different components draw on different ones.
--ehu-color-* · brand primitives
20 well-named primitives (purple, heliotrope, grass-green, maple-red…). Defined in ehu-wordpress-colors.css. This is the right foundation.
--ehu-colors-* · legacy semantic
~80 aliases (primary-darkest, notice-warning-lighter, heritage-*) left over from an earlier palette and remapped to the neon values. Many resolve to the same hex value.
--wp--preset--* · WordPress core
The editor palette still exposes WordPress defaults (vivid-cyan-blue, pale-pink, luminous-vivid-amber), plus primary:#fff and secondary:#2D2926, a legacy charcoal.
Naming consistency
| Issue | Evidence | Recommendation | Severity |
|---|---|---|---|
| Singular and plural namespaces side by side | --ehu-color-primary vs --ehu-colors-primary, both #3a0c51 | Keep --ehu-color-* for primitives. Add --ehu-role-* (or --ehu-sys-*) for semantics. Deprecate -colors-. | High |
| “Heritage” names pointing to neon values | heritage-purple → #3a0c51, heritage-yellow → marigold | Rename by role, not by history. Map old names through a deprecation alias file. | Med |
| Names that don't match the value | --ehu-colors-sand = #ffba1b (marigold). --ehu-font-family-serif = a sans-serif stack. secondary-red-light = heliotrope (purple). | Audit every alias against its value. Rename or remove the misleading ones. | Med |
| Component tokens without a prefix | --border-color, --padding, --font-size:18px, --text-color:#111 (TablePress / theme) | Scope component tokens: --ehu-table-border etc. | Low |
| Two size vocabularies | T-shirt sizes (xs…xxxxl, hero-plus) alongside WP numeric presets (spacing--20…80) | Pick one. Numeric steps scale better than xxxxl. | Low |
Broken references
These tokens are referenced with var() but never defined in any stylesheet the site loads, so they silently fall back to their initial value:
--ehu-color-suffragette --ehu-font-size-md --ehu-cover-title-width (may be set inline per block)
66 --ehu-* tokens are defined but never referenced. Most are the duplicated -dark/-darker/-darkest steps.
Colour
These are the 20 brand primitives in --ehu-color-*. Each tile shows the colour's contrast against white and black.
primary, primary-dark, -darker, -darkest and -accent all equal #3a0c51, and all seven notice-warning-* steps equal #ffba1b. Hover and active states built on these tokens therefore show no visible change, and a designer choosing “darker” in the editor gets the same colour.Collapsed ramps
| Semantic group | Steps defined | Distinct values | Resolves to |
|---|---|---|---|
--ehu-colors-primary* | 8 | 3 | #3a0c51 ×5, #8c40c4 ×3, #d883ff ×1 |
--ehu-colors-notice-warning* | 7 | 1 | #ffba1b |
--ehu-colors-secondary-yellow* | 7 | 1 | #ffba1b |
--ehu-colors-notice-error* | 7 | 3 | #e04528 ×5, #53133e (maroon), #d883ff (a purple used as “lightest error”) |
--ehu-colors-secondary-green* | 7 | 2 | #1e8443, #12372e |
--ehu-colors-neutral* | 9 | 4 | dark = darker = darkest = #b3b3b3. lighter = lightest = tabs = #fff |
--ehu-colors-link* | 4 | 1 | #3a0c51: no visited, hover or active difference |
Conflicting definitions
ehu-cover.css redefines seven global tokens with values from the previous palette. Which value wins depends on stylesheet load order and specificity, so pages that load the cover block may render differently from pages that don't.
| Token | ehu-wordpress-colors.css | ehu-cover.css |
|---|---|---|
--ehu-colors-heritage-purple | ■ #3a0c51 | ■ #5f295f |
--ehu-colors-primary-light | ■ #8c40c4 | ■ #875f87 |
--ehu-colors-deep-purple | ■ #3a0c51 | ■ #21191c |
--ehu-colors-sand | ■ #ffba1b | ■ #f0d283 |
--ehu-colors-outline-yellow | ■ #ffba1b | ■ #fedd00 |
--ehu-colors-notice-warning | ■ #ffba1b | ■ #dba617 |
--ehu-colors-text-black | #000 | #1d1d1d |
The legacy translucent overlays --ehu-trans-primary (rgba 95,41,95), --ehu-trans-blue and --ehu-trans-green are also still built from the old heritage hues.
Hardcoded colour
82% of the 1,083 colour declarations use var(), which is a solid result. The other 73 contain raw hex values (50 distinct). Notable ones: #ffe63c, #ed1f34 and #f9effb, which are not in the palette. There are also 11 different light greys (#ddd, #eee, #f0f0f0, #f8f8f8, #fafafa, #e0e0e0…) and un-themed vendor blues in TablePress (#d9edf7, #049cdb).
Contrast: pairings used on the site
Recommendation: publish an approved-pairings matrix and limit text-on-colour combinations to it in the block editor. Heliotrope, grass, sky and marigold should be documented as surface colours, never text on white. Maple red as an error colour is only safe at ≥24px (or ≥18.66px bold), so add a darker error-text token such as #b8321a.
Typography
The typeface is Caslon Doric, served from Adobe Fonts in weights 300–700, with Public Sans as the fallback. The geometric grotesque suits the neon brand well.
The tokenised scale is a clean 1.2 minor third on an 18px root. The CSS also contains a second 1.25 major third scale (1.563, 1.953, 2.441, 3.052rem) and dozens of one-off sizes.
Font-family declarations
| Stack | Uses |
|---|---|
'Open Sans' (not loaded) | 20 |
var(--ehu-font-family-primary) | 14 |
'Public Sans' on its own | 12 |
Caslon Doric, Public Sans (+ serif / Montserrat / Inter variants) | 16 |
Body text falls back to serif even though both named faces are sans-serif. If Adobe Fonts fails, users may see Times. Two icon fonts load as well (Font Awesome 6 Pro and Google Material Icons).
Defined scale (--ehu-font-size-*)
Usage against the scale
| Property | Token defined | Uses via token | Hardcoded | Notes |
|---|---|---|---|---|
| font-size | 12 steps | ~104 (40%) | ~157 · 64 distinct values | .875rem ×20, 1.25rem ×12, 1.5rem ×11, plus px values (16, 20, 28, 35, 150px) |
| font-weight | 5 steps | 93 (55%) | 75 | Raw 700 appears 47 times |
| line-height | 6 steps | 8 (13%) | ~52 · 17 distinct | 1.15, 1.2, 1.25, 1.55, 1.9, 2.2, 20px, 6rem… |
| letter-spacing | 3 steps | rare | — | — |
Rendered headings on the homepage
| Element | Size / weight / line-height | Finding |
|---|---|---|
| body | 18px / 300 / 1.55 | Med Light 300 weight for long-form body text reduces legibility, especially on low-DPI Windows screens. Consider 400. |
| h1 (hero) | 59.6px / 700 / 1.25 | Matches hero. Good. |
| h2 · section | 43.9px / 500 / 1.2 | High Three H2 treatments on one page (sizes 44, 37 and 26px, weights 500, 600 and 400). Heading level is being used for styling rather than structure. |
| h2 · promo | 37.3px / 600 / 1.2 | |
| h2 · news | 25.9px / 400 / 1.2 | |
| h3 · cards | 18px / 500 / 1.2 | Low Same size as body copy, so the hierarchy depends on weight alone. |
Spacing & layout
Two spacing scales
| EHU tokens | WP presets |
|---|---|
| xs 0.25rem | 20 · 0.44rem |
| sm 0.75rem | 30 · 0.67rem |
| md 1.5rem | 40 · 1rem |
| lg 3rem | 50 · 1.5rem |
| xl 4rem | 60 · 2.25rem · 70 · 3.38rem · 80 · 5.06rem |
The EHU scale is used 87 times across 1,292 spacing declarations (under 10% of non-zero values). In practice the de facto rhythm is .75rem / 1.5rem / 3rem, which matches the tokens but is typed by hand. There are 236 distinct padding, margin and gap values in total, including 12px, 24px, .375rem, 1.2rem and 2.5rem.
Content width is 1200px (--wp--style--global--content-size), which is sensible.
Breakpoints: 77 distinct media queries
Breakpoints cluster in groups of values one pixel apart, a sign that each component was written independently:
| Intended | Values actually used |
|---|---|
| Small | 320/321, 389/390/391, 499, 600, 680 |
| Tablet | 766, 767, 768, 769 |
| Laptop | 1000, 1023, 1024, 1025, 1026 |
| Desktop | 1142, 1143, 1144, 1441, 1500, 1501, 1520, 1601, 1919 |
Overlapping min/max pairs (e.g. max-width:767px vs max-width:766px) leave 1px gaps where neither rule applies. Standardise on 4–5 breakpoints (e.g. 390, 768, 1144, 1500) as Sass/PostCSS custom media.
Shape & motion
Signature: the offset block shadow
The coloured hard shadow (var(--ehu-color-*) -18px 18px) used on images and promo panels is the most distinctive element of the neon look. It is used with five colours but is not tokenised. Promote it to --ehu-shadow-offset-{sm,lg} with a colour variable.
Radius: square by intent, not by rule
Most elements have 0 radius, which is the brand intent. But 7 other values exist (3px, 4px, 5px, .3125rem, .625rem, 50px, 50px 50px 50px 0). Define --ehu-radius-none and --ehu-radius-pill only.
Shadows: 17 bespoke values
WordPress shadow presets (natural, deep, sharp, crisp, outlined) are defined but used 0 times. Soft drop shadows such as 0 4px 8px rgba(0,0,0,.2), 0 6px 20px… clash with the flat brand.
Motion tokens unused
--ehu-transition-fast and -normal are defined but never used. There are 24 distinct transition values (.2s–1s), and most use transition: all. Only 1 of 34 stylesheets respects prefers-reduced-motion.
z-index
There are 16 distinct values: 1, 2, 3, 4, 5, 9, 10, 70, 75, 100, 999, 10000, 100000, 999999 and others. Replace them with a named layer scale (base · dropdown · sticky · overlay · modal · toast).
Component inventory
Components are built as bespoke ehu-blocks (Lazy Blocks) on top of WordPress core blocks. Each ships its own stylesheet. Scores are based on states, variants and token use visible in the CSS.
| Component | States | Variants | Token use | Notes | Score |
|---|---|---|---|---|---|
Buttons ehu-buttons | ✅ hover · ⚠ focus | ✅ colour ×6, size ×12 | ⚠ | Hover differs by variant: some use filter:brightness(.85), some underline, some change background with !important. The weight is 600 on “Book” but 400 on “All news”. | 6/10 |
Cover / hero ehu-cover | ✅ | ✅ many | ❌ | 39 KB of CSS. Redefines global colour tokens (see §03). Two --stage-height values. | 5/10 |
Card ehu-card | ✅ hover | ✅ event / news | ✅ | Good brand use. Card H3s match body size. | 7/10 |
| Promo panel | ✅ | ✅ colourways | ✅ | Best showcase of the offset shadow. 17 !important. | 7/10 |
CTA ehu-cta | ✅ | ⚠ | ⚠ | Overlaps with Buttons. Consider merging. | 5/10 |
| Course search (Algolia) | ✅ | — | ⚠ | 25 KB of separate search CSS with its own greys. | 6/10 |
| News list · Course list · Subject list · Department list · Research-unit list | ✅ | ⚠ | ⚠ | Five list blocks with similar anatomy. A candidate for one ehu-list pattern with modifiers. | 5/10 |
| Icon panel · Tab heading · Sticky in-page menu | ⚠ | ⚠ | ⚠ | Small and focused. | 6/10 |
| Modal (iziModal) | ✅ | — | ❌ | 93 KB vendor CSS for a 244-byte theme wrapper. | 4/10 |
| Toast (Notyf) | ✅ | ✅ | ❌ | Vendor defaults. Not themed to brand success/error. | 4/10 |
| Carousel (Slick 1.8.1, CDN) | ⚠ | — | ❌ | Unmaintained library. Default theme loaded from jsDelivr. | 3/10 |
| Tables (TablePress) | ⚠ | — | ❌ | Vendor blue header #d9edf7 / #049cdb, which is off-brand. | 3/10 |
| Timetable (MP Timetable) | ⚠ | — | ❌ | Unthemed vendor styling. | 3/10 |
Buttons as rendered
These are four buttons from one page, with two text colours on the same lime fill, two weights and two paddings. Define primary, secondary and tertiary variants with fixed text colour, weight and padding. Leave background choice to the colourway modifier.
Accessibility
| Check | Result | Status |
|---|---|---|
| Skip link | Skip Navigation → #content present | Pass |
| Document language | Set | Pass |
| Image alt attributes | 15/15 homepage images have alt text | Pass |
| Heading outline | 10 <h2>s in the header/mega-menu come before the page <h1> | Fix |
| Focus styles | 172 :focus rules and 30 :focus-visible rules, but 10 outline:none/0. Focus ring colour varies (marigold, primary, default). | Review |
| Reduced motion | Respected in 1 of 34 stylesheets | Fix |
| Colour contrast | Core pairings pass. Heliotrope, grass, sky, marigold and Black 30 fail as text on white. Maple and iris are AA-large only. | Govern |
| Body weight | 300 at 18px | Review |
This is an automated, foundations-level review. A full WCAG 2.2 AA audit would need keyboard, screen-reader and zoom testing across templates.
CSS health
317 !important
Concentrated in styles.css (87), inline WP styles (87), block-margin.css (40) and customiser.css (30). The theme and the blocks are overriding each other.
34 stylesheets · ~416 KB
Each block loads its own file. Vendor CSS (iziModal 93 KB, Algolia 25 KB) makes up a large share. Slick loads from a third-party CDN.
Admin CSS on the front end
ehu-wordpress-admin.css loads for all public visitors. Scope it to is_admin() / the editor.
Roadmap
The actions are ordered by impact relative to effort. Steps 1–3 fix the foundations, and the rest build on them.
Consolidate colour into two tiers
Keep --ehu-color-* primitives. Create a small semantic tier (text, surface, accent, link, focus, success/warning/error) with real hover/active values. Move all --ehu-colors-* into a deprecated alias file, and remove the conflicting redefinitions in ehu-cover.css.
~1 sprint
Replace the WordPress editor palette
Override theme.json so editors see only the neon palette and approved gradients. Remove the core defaults (vivid-cyan, pale-pink…) and the legacy #2D2926.
Days
Lock typography to one scale
Use the 1.2 scale only. Remove the 1.25 values and 'Open Sans'. Fix the fallback to sans-serif. Define heading roles (display, h1–h4) with fixed size and weight, and test body text at 400.
~1 sprint
Standardise breakpoints & spacing
Use 4–5 named breakpoints as custom media. Replace hand-typed .75/1.5/3rem with spacing tokens using a codemod, and choose one scale (EHU or WP), not both.
1–2 sprints
Tokenise the signature elements
Add offset shadow, underline rule, tag/eyebrow, focus ring (marigold, 3–4px), radius-none/pill, motion durations and z-index layers. Add a global prefers-reduced-motion rule.
Days
Rationalise components
Merge CTA into Buttons with three variants. Unify the five list blocks. Theme or replace the vendor modal, toast, carousel and table styles. Drop Slick.
Quarter
Reduce !important with cascade layers
Introduce @layer reset, wp, tokens, base, blocks, utilities so blocks beat theme defaults by order rather than by force. Bundle the block CSS and stop loading admin CSS on the front end.
Ongoing
Document & govern
Publish a living reference (Storybook or a WP pattern library page) covering tokens, approved colour pairings, component do's and don'ts, and a deprecation log. Add Stylelint rules: no raw hex, no unlisted font sizes, no new !important.
Ongoing
Methodology
- Pages sampled:
/,/courses/,/study/,/news/,/departments/ - All linked stylesheets and inline
<style>blocks were downloaded and parsed. Font Awesome and Slick were excluded from the counts. - Tokens extracted with regex. Usage counts are approximate and exclude vendor files (iziModal, Notyf, TablePress, MP Timetable, admin) unless noted.
- Computed styles and screenshots taken from the live homepage in Chrome at 1432px viewport.
- Contrast calculated with the WCAG 2.x relative-luminance formula.
- Scores reflect public front-end evidence only. Internal docs and Figma libraries were not available.