# Project Requirements

> **This is the shared reference for all component work on this project.**

---

## Table of Contents

- [Project Requirements](#project-requirements)
	- [Table of Contents](#table-of-contents)
	- [1.0 Project Overview](#10-project-overview)
		- [1.1 Project Description](#11-project-description)
		- [1.2 Development Responsibilities](#12-development-responsibilities)
	- [2.0 Tech Stack](#20-tech-stack)
		- [2.1 Folder structure notes](#21-folder-structure-notes)
			- [2.1.1 CSS folders](#211-css-folders)
		- [2.2 Branch conventions](#22-branch-conventions)
	- [3.0 Gutenberg features](#30-gutenberg-features)
	- [4.0 Component architecture](#40-component-architecture)
	- [5.0 Site Data - Custom Post Types](#50-site-data---custom-post-types)
		- [5.1 Team members](#51-team-members)
		- [5.2 Transactions](#52-transactions)
		- [5.3 Testimonials](#53-testimonials)
		- [5.4 Courses](#54-courses)
	- [6.0 Design References](#60-design-references)
		- [6.1 Figma](#61-figma)
			- [6.1.1 Figjam](#611-figjam)
		- [6.2 Brand Guide](#62-brand-guide)
		- [6.3 Typography](#63-typography)
			- [6.3.1 Font use](#631-font-use)
		- [6.4 Color tokens](#64-color-tokens)
		- [6.5 Spacing tokens](#65-spacing-tokens)
	- [7.0 Where logic goes](#70-where-logic-goes)
	- [8.0 Global Standards](#80-global-standards)
		- [8.1 Browsers \& devices](#81-browsers--devices)
		- [8.2 Accessibility](#82-accessibility)
		- [8.3 Coding conventions](#83-coding-conventions)
			- [8.3.1 CSS](#831-css)
			- [8.3.2 Javascript](#832-javascript)
			- [8.3.3 PHP](#833-php)

---

## 1.0 Project Overview

| | |
|---|---|
| **Project / Site Name** | Heritage Title Company of Austin |
| **Client** | Heritage Title |
| **Figma File** | [Dev Mode Like](https://www.figma.com/design/W8B1QwA11DLURYOVyVqDE7/26-HTCO-1225-Heritage-Title-Website?node-id=4160-13&m=dev) |
| **Figjam File** | [Components breakdown](https://www.figma.com/board/q57IkMkoggsAl8pBuFsXME/Heritage-Title-Components?node-id=0-1&t=oyACy2piIPyGmJLF-1) |
| **Brand Guide** | [Simplified brand guide pdf](https://www.dropbox.com/scl/fi/bs2xnnsfb5nbtq8kv2gee/25-HTCO-1227P-Mini-Brand-Guide-Simplified.pdf?rlkey=cf0a7jumur7d7casvycrg18i4&st=nggh6817&dl=0)|
| **Staging URL** | https://htco.catalyst-austin.com/ |
| **Target Launch Date** | TBD |
| **Scope** | This theme/repo will not be reused for other projects. |

### 1.1 Project Description

A WordPress website to replace the company's current [corporate website](https://heritagetitleofaustin.com/) with a fresh design and easier navigation. Most users are residential real estate agents, buyers, and commercial real estate brokers. The title company provides courses for real estate agents who require continuing education hours to renew their license. Title insurance pricing is set by the state so their unique selling proposition is their service and experience.  

An intuitive, simplified, and performant authoring experience is important. We will likely hand this site off to the client to host with their own hosting provider. The client will be making edits and building new pages on their own in the future. 

### 1.2 Development Responsibilities

- Ranium will build blocks, block patterns, and the custom post type data relationships.
- Catalyst will test the blocks, block patterns, and use them to create the layouts in the Figma design to ensure they will be easy for the client to use in the future.
- Ranium will also build a title policy pricing calculator feature after the main components are complete.
  - The pricing calculator will likely be easiest to build as a Vue or React component that can be embedded in a page or added as a stand alone block. Catalyst will provide more details after further discussions with the client about requirements.

---

## 2.0 Tech Stack

| Layer | Detail |
|---|---|
| **WordPress - Gutenberg editor** | Bedrock (Composer-managed, non-standard folder structure — see below) |
| **Theme** | Sage — Blade templates, Tailwind CSS, Vite build |
| **Laravel layer** | Acorn — service providers, View Composers, routing |
| **PHP** | >= 8.3 |
| **Node** | >= 24 |
| **Deployment** | TBD |

### 2.1 Folder structure notes

- Use Roots.io Bedrock project structure.
- Do not assume standard WordPress paths. Always use WordPress helper functions (`get_template_directory_uri()`, `home_url()`, etc.) rather than hardcoding paths.

#### 2.1.1 CSS folders

```sh
resources/css/
├── app.css              # Entry point — imports + @theme
├── tokens.css           # All custom properties
├── base.css             # Element defaults (H1–H6, body, etc.)
├── components/
│   ├── card.css
│   ├── section.css
│   └── ...
└── editor.css
```

---

### 2.2 Branch conventions

| | |
|---|---|
| **main** | production ready code |
| **develop** | code in progress |
| **feature-x** | components and features |
| **staging** | production pending final testing |

---

## 3.0 Gutenberg features

- **3.0.1** Author should only be able to use colors from the tokens
- **3.0.2** Author should only be able to use fonts listed in the brand guide and Figma design
- **3.0.3** Editor view should reflect the live view as closely as possible, but at a minimum should reflect when items are displaye in columns, full bleed, background colors, and text colors
  - **3.0.3.1** Components that can animate like carousels and hover/focus effects do NOT have to reflect the same functionality in the editor.
- **3.0.4** Author should be able to set spacing using a scale that applies a spacing token. 

### 3.1 ACF Blocks

- **3.1.1** Do not enable ACF's inline editing feature for ACF Blocks which was introduced in ACF Pro >= 6.7. The feature is functional but documented edge cases are a real risk when using blade templates.
	- Empty fields receive placeholder text which could interfere with blade template conditional logic
	- Any value reshaped between get and output in a blade template won't be inline editable. For the user this will likely result in several fields appearing to be randomly inline editable and cause frustration. 
	- Fields with identical values rendered in a different order than fetched can bind the wrong field to the wrong element when editing.
- **3.1.2** Use v3 blocks to ensure the new iframe editor experience is enabled. The iframe editor dramatically reduces editor style conflicts with theme styles, particularly when the theme uses CSS layers.
- **3.1.3** Opt for using Gutenberg's core fields when possible unless an ACF field is needed for precise control.
	- examples: color field and background tied to theme registered colors and gradients instead of ACF's color picker.
- **3.1.4** When a component can be created easily, while maintaining proper semantic HTML, with core blocks — register a pattern in the theme code rather than building a custom block. 

---

## 4.0 Component architecture

- **4.1** Use native Gutenberg blocks when they are well suited.
- **4.2** Use block patterns saved as code for components that do not need to pull content from custom post types metadata.
- **4.3** Use ACF block builder and ACF fields to create custom blocks for components that need to pull content from custom post types metadata.
- **4.4** All components should have the ability to add custom CSS classes in the editor
- **4.5** Blocks should generate HTML in post_content, not postmeta.

---

## 5.0 Site Data - Custom Post Types

### 5.1 Team members

Team member post type will the following fields:

- **5.1.1** First name
- **5.1.2** Last name
- **5.1.3** Email
- **5.1.4** Phone
- **5.1.5** Bio
- **5.1.6** Headshot
- **5.1.7** Role

Team members can have the following taxonomies:

- **5.1.8** Location(s)
- **5.1.9** Team(s)

### 5.2 Transactions

Transaction post type with the following fields:

- **5.2.1** Project name
- **5.2.2** Project image

Transactions can have the following taxonomies:

- **5.2.3** Project type
- **5.2.4** Market city
- **5.2.5** Market state

### 5.3 Testimonials

Testimonial post type with the following fields:

- **5.3.1** Headshot
- **5.3.2** Name
- **5.3.3** Title
- **5.3.4** Client description (short text like "Cousins Properties, Austin, TX")
- **5.3.5** Copy (the actual block of testimonial text)

Testimonials can have the following taxonomies:

- **5.3.6** Team(s) (the same "team" taxonomy used by Team Members, e.g. "Residential", "Commercial")

### 5.4 Courses

Course custom post type with the following fields:

- **5.4.1** Title
- **5.4.2** Description
- **5.4.3** CE Code (short text for the continuing education course identifier)
- **5.4.4** Image
- **5.4.5** Date
- **5.4.6** Time

Courses can have the following taxonomies:

- **5.4.7** Location (these will be the same locations that the Team Members can be associated with)

---

## 6.0 Design References

### 6.1 Figma

- **6.1.1** All page designs are in the Figma file linked above
  - **6.1.1.1** Reference the page designs for component design requirements
- **6.1.2** Use Figma's Inspect panel for exact font family, font weight, and colors
- **6.1.3** Do NOT use Figma's values for spacing and font sizing. Spacing and font sizing should be fluid 
- **6.1.4** If a state is missing from Figma (hover, empty, error, loading) — log it in the [open questions](#open-questions) before building

#### 6.1.1 Figjam

See [Heritage Title Components Figjam](https://www.figma.com/board/q57IkMkoggsAl8pBuFsXME/Heritage-Title-Components?t=p5XzHYRNKqNmW9r8-1) for a breakdown of the site's components

### 6.2 Brand Guide

The brand guide PDF is linked above. It covers color palette and typography. When Figma and the brand guide conflict, rely on Figma.

### 6.3 Typography

Fonts are loaded from `resources/fonts/` and registered in `app.css`. Do not pull fonts from Google CDN or any external source — use the local files only.

- The required font files can be downloaded from [Google Drive](https://drive.google.com/drive/folders/13P4WLtOVMMFe76fJFmqPFxhVWEJqAsCA?usp=sharing) if needed.

#### 6.3.1 Font use

| Role | Font Family | Weight |
|---|---|---|
| Headings - default | Poppins | 500 |
| Headings - big | **Besley** | 700 |
| Headings - accent | _Merriweather Italic_ | 400 |
| Subheadings | _Merriweather Italic_ | 400 |
| Eyebrow text | _Merriweather Italic_ | 400 |
| Body copy | Poppins | 400 |
| CTA | **Poppins** | 600 |
| Primary Nav link text | **Poppins** | 600 |
| Footer Nav link text | Poppins | 400 |

### 6.4 Color tokens

| Role | Hex |
|---|---|
| brand | #BA9634 |
| accent | #0D2440 |
| surface | #ffffff |
| surface-subtle | #F2F3F6 |
| surface-dark | #58585A |
| surface-frame | #ACACAF |
| text | #222222 |
| muted | #727274 |
| soft | #E7E1DB | 
| organic | #A29078 |

- NOTE: soft and organic are not currently in use in the mockup

> Colors should be defined in `tokens.css`. Do not hardcode hex values in component templates — always use the Tailwind token.

### 6.5 Spacing tokens

The system must support named spacing scales ("steps") rather than per-value spacing inputs, to limit authoring variation and enforce brand conventions. See [Utopia Fluid](https://utopia.fyi/space/calculator/?c=360,18,1.2,1240,20,1.25,5,2,&s=0.75%7C0.5%7C0.25,1.5%7C2%7C3%7C4%7C6,s-l&g=s,l,xl,12) Design for value generation.

Example:
```css
	/* Fluid font sizing — https://utopia.fyi/type/calculator?c=360,15,1.125,4200,18,1.25,10,7 */
    --FS-7: clamp(0.2359rem, 0.4275rem + -0.073vw, 0.4111rem);
    --FS-6: clamp(0.2949rem, 0.4781rem + -0.0698vw, 0.4624rem);
    --FS-5: clamp(0.3686rem, 0.5345rem + -0.0632vw, 0.5202rem);
    --FS-4: clamp(0.4608rem, 0.5969rem + -0.0519vw, 0.5853rem);
    --FS-3: clamp(0.576rem, 0.6662rem + -0.0343vw, 0.6584rem);
    --FS-2: clamp(0.72rem, 0.7427rem + -0.0086vw, 0.7407rem);
    --FS-1: clamp(0.8333rem, 0.8271rem + 0.0278vw, 0.9rem);
    --FS0: clamp(0.9375rem, 0.9199rem + 0.0781vw, 1.125rem);
    --FS1: clamp(1.0547rem, 1.0217rem + 0.1465vw, 1.4063rem);
    --FS2: clamp(1.1865rem, 1.133rem + 0.238vw, 1.7578rem);
    --FS3: clamp(1.3348rem, 1.254rem + 0.3593vw, 2.1973rem);
    --FS4: clamp(1.5017rem, 1.385rem + 0.5187vw, 2.7466rem);
    --FS5: clamp(1.6894rem, 1.5259rem + 0.7266vw, 3.4332rem);
    --FS6: clamp(1.9006rem, 1.6764rem + 0.9962vw, 4.2915rem);
    --FS7: clamp(2.1382rem, 1.8357rem + 1.3443vw, 5.3644rem);
    --FS8: clamp(2.4054rem, 2.0023rem + 1.7917vw, 6.7055rem);
    --FS9: clamp(2.7061rem, 2.174rem + 2.3649vw, 8.3819rem);
    --FS10: clamp(3.0444rem, 2.3475rem + 3.0971vw, 10.4774rem);

    /* Fluid padding */
    --PAD-7: clamp(0.88rem, calc(0.79rem + 0.27vw), 1.12rem);
    --PAD-6: clamp(0.99rem, calc(0.86rem + 0.40vw), 1.34rem);
    --PAD-5: clamp(1.11rem, calc(0.93rem + 0.57vw), 1.61rem);
    --PAD-4: clamp(1.25rem, calc(1.00rem + 0.78vw), 1.93rem);
    --PAD-3: clamp(1.40rem, calc(1.07rem + 1.04vw), 2.32rem);
    --PAD-2: clamp(1.58rem, calc(1.14rem + 1.37vw), 2.78rem);
    --PAD-1: clamp(1.78rem, calc(1.20rem + 1.78vw), 3.33rem);
    --PAD0: clamp(2.00rem, calc(1.26rem + 2.29vw), 4.00rem);
    --PAD1: clamp(2.25rem, calc(1.30rem + 2.91vw), 4.80rem);
    --PAD2: clamp(2.53rem, calc(1.33rem + 3.69vw), 5.76rem);
    --PAD3: clamp(2.85rem, calc(1.34rem + 4.64vw), 6.91rem);
    --PAD4: clamp(3.20rem, calc(1.31rem + 5.82vw), 8.29rem);
    --PAD5: clamp(3.60rem, calc(1.25rem + 7.26vw), 9.95rem);
    --PAD6: clamp(4.05rem, calc(1.12rem + 9.02vw), 11.94rem);
    --PAD7: clamp(4.56rem, calc(0.93rem + 11.17vw), 14.33rem);
    --PAD-ZERO: 0;

    /* Fluid spacing — https://utopia.fyi/space/calculator?c=320,16,1,1440,20,1,3,2
       Base 16px→20px across 320–1440px viewport. T-shirt scale ×0.25–×6 of base. */
    --space-3xs: clamp(0.25rem, 0.232rem + 0.089vw, 0.3125rem);
    --space-2xs: clamp(0.5rem,  0.464rem + 0.179vw, 0.625rem);
    --space-xs:  clamp(0.75rem, 0.696rem + 0.268vw, 0.9375rem);
    --space-s:   clamp(1rem,    0.929rem + 0.357vw, 1.25rem);
    --space-m:   clamp(1.5rem,  1.393rem + 0.536vw, 1.875rem);
    --space-l:   clamp(2rem,    1.857rem + 0.714vw, 2.5rem);
    --space-xl:  clamp(3rem,    2.786rem + 1.071vw, 3.75rem);
    --space-2xl: clamp(4rem,    3.714rem + 1.429vw, 5rem);
    --space-3xl: clamp(6rem,    5.571rem + 2.143vw, 7.5rem);
    /* One-up pairs — smooth transition between adjacent steps */
    --space-3xs-2xs: clamp(0.25rem,  0.143rem + 0.536vw, 0.625rem);
    --space-2xs-xs:  clamp(0.5rem,   0.375rem + 0.625vw, 0.9375rem);
    --space-xs-s:    clamp(0.75rem,  0.607rem + 0.714vw, 1.25rem);
    --space-s-m:     clamp(1rem,     0.75rem  + 1.25vw,  1.875rem);
    --space-m-l:     clamp(1.5rem,   1.214rem + 1.429vw, 2.5rem);
    --space-l-xl:    clamp(2rem,     1.5rem   + 2.5vw,   3.75rem);
    --space-xl-2xl:  clamp(3rem,     2.429rem + 2.857vw, 5rem);
    --space-2xl-3xl: clamp(4rem,     3rem     + 5vw,     7.5rem);
```

---

## 7.0 Where logic goes

- **7.1 Display only** → Blade template in `resources/views/`
- **7.2 Data preparation** → View Composer in `app/View/Composers/`
- **7.3 Reusable UI with logic** → Blade Component in `app/View/Components/` + `resources/views/components/`
- **7.4 WordPress hooks/filters** → `app/filters.php` or a Service Provider

---

## 8.0 Global Standards

### 8.1 Browsers & devices

- **8.1.1** Chrome, Firefox, Safari, Edge — latest two versions
- **8.1.2** iOS Safari 16+, Android Chrome — latest
- **8.1.3** No IE11 support

### 8.2 Accessibility

- **8.2.1** All images should be able to pull `alt` text from the media library (or `alt=""` if no alt text has been entered in the media library)
- **8.2.2** Color contrast must meet WCAG 2.1 AA (4.5:1 body text, 3:1 large text)
- **8.2.3** All interactive elements must be keyboard-accessible with visible focus states
- **8.2.4** All `<section>` elements should have a heading to label them or the option to include a label via aria-label attribute 
- **8.2.5** Carousels should be built with an accessible framework. We recommend [embla](https://github.com/davidjerleke/embla-carousel) or a plugin built on embla but open to suggestions.
- **8.2.6** Form fields must have associated `<label>` elements but can be hidden with sr-only

### 8.3 Coding conventions

- **8.3.1** Follow the existing Sage theme structure — do not add files outside established directories
- **8.3.2** Blade templates in `resources/views/`, data logic in `app/View/` Composers
- **8.3.3** No inline styles
- **8.3.4** No hardcoded URLs — use WordPress helper functions
- **8.3.5** No hardcoded color values — use Tailwind tokens
- **8.3.6** For CSS classes and any variables referring to multiple items, prefer to append the name with "list" or "collection" rather than making the name plural. Example: floorPlanCardList, $cta_collection, etc. vs floorPlanCards or $ctas.

#### 8.3.1 CSS

- **8.3.1.1** Do not use tailwind/functional CSS for all styles. Tailwind should be relied upon for their build configuration, design token conventions, and functional classes when those make the most sense to use
- **8.3.1.2** Apply default styles to semantic elements like `<section>`, `<nav>`, `<p>`, headings, `<a>`, and `<button>`, etc.
- **8.3.1.3** Do not be afraid to chain selectors to style semantic elements based on context, like the difference between an anchor inside an Nav versus an anchor inside a paragraph.
- **8.3.1.4** Avoid using media queries in almost all circumstances. The only appropriate use cases for media queries are when you're targeting device width below a certain size because you know there is no context in which your layout would work any other way once a device gets that small. For example: if you know that even if your component takes up the full width of the screen, if the screen is less than 500px wide, then you have to override certain layout values.
- **8.3.1.5** Do not make context assumptions when styling components. Instead, rely on modern CSS features like :has or :is and container queries. If CSS cannot determine the context, then use JavaScript to obtain it rather than making an assumption.
- **8.3.1.6** For all components that involve Text approach layout with the assumption that the user can increase or decrease their default font size.

#### 8.3.2 Javascript

- **8.3.2.1** Use plain JavaScript rather than JQuery.
- **8.3.2.2** Functional JavaScript is preffered.

#### 8.3.3 PHP

- **8.3.3.1** Rely on Sage/Laravel conventions whenever possible.
