Getting Started — USTA & USTA Foundation
A developer guide for setting up your local environment, understanding the project architecture, and working with content across both EDS sites built on the AEM Block Collection.
Project overview
This project supports two production sites from a single GitHub repository using a repoless architecture — there is no fstab.yaml; content routing is handled entirely through Document Authoring (DA) org configuration.
| Attribute | Value |
|---|---|
| GitHub org | AdobeDevXSC |
| Repository | ema-usta |
| Primary branch | main |
| Architecture | Repoless EDS (no fstab.yaml) |
| Content system | DA (Document Authoring via da.live) |
| Base boilerplate | aemsites/da-block-collection |
| Code: JS / CSS / HTML | 73% / 26% / 1% |
Key URLs
GitHub
Document Authoring (DA)
Preview & live environments
{branch}--{repo}--{org}.aem.page (preview) or .aem.live (published). Swap main for any branch name to preview your feature branch.
Architecture
Both sites share a single codebase. EDS resolves which DA content org to serve based on the hostname — the repoless pattern maps each domain to its DA org without a checked-in fstab.yaml.
Single shared codebase
Repository structure
Local development
Prerequisites
| Tool | Install | Notes |
|---|---|---|
| Node.js 20+ | nodejs.org | Required for AEM CLI and linting |
| AEM CLI | sudo npm install -g @adobe/aem-cli | Provides aem up local server |
| npm packages | npm i | ESLint, Stylelint, Husky pre-commit hook |
| AEM Code Sync | GitHub App | Syncs code changes to AEM on push |
Step-by-step setup
-
1Clone the repository
Clone to your local machine and open in your editor of choice.
git clone https://github.com/AdobeDevXSC/ema-usta.git cd ema-usta -
2Install the AEM CLI
The AEM CLI provides a local development server that proxies content from DA and code from your local filesystem. Install it globally via npm:
sudo npm install -g @adobe/aem-cliVerify the install:
aem --versionIf
aemis not found after install, make sure your global npm bin is on yourPATH— find it withnpm bin -g. -
3Install npm packages
This sets up linting (ESLint + Stylelint) and the Husky pre-commit hook that auto-regenerates Universal Editor component JSON.
npm i -
4Start the local dev server
Run
aem upwith the--urlflag pointing to the site you want to work against. Each site has its own DA content org and preview URL:# USTA — DA: da.live/#/adobedevxsc/ema-usta aem up --url=https://main--ema-usta--adobedevxsc.aem.page/ # USTA Foundation — DA: da.live/#/adobedevxsc/usta-foundation aem up --url=https://main--ustafoundation--adobedevxsc.aem.page/This opens
http://localhost:3000and proxies content from the specified DA preview environment. Your local/blocks,/scripts, and/stylesare served from disk against whichever site's content you've targeted.
GitHub workflow
CI pipeline
Every push to any branch triggers the GitHub Actions lint workflow (.github/workflows/main.yaml). It runs on Node.js 20 and executes npm run lint — which covers both ESLint (JS) and Stylelint (CSS). PRs should be green before merging to main.
Pre-commit hook
Husky runs npm run build:json on every commit. This regenerates the merged Universal Editor component JSON files (component-definition.json, component-filters.json, component-models.json) from the per-block model sources in /ue/models/blocks/. Never edit the root JSON files directly — they are auto-generated.
Previewing a PR branch
Every branch has its own preview URL automatically. Substitute the branch name into the EDS URL pattern:
feature/nav-update becomes feature-nav-update--ema-usta--adobedevxsc.aem.page.
AEM Code Sync
The AEM Code Sync GitHub App must be installed on the AdobeDevXSC org and granted access to this repository. It watches for pushes and syncs code to the AEM CDN, making your branch immediately available at the preview URL above.
Document Authoring (DA)
All page content is managed through DA at da.live. There are two separate DA content orgs — one per site — both mapped to the same GitHub repository.
Org structure
| Site | DA org | Content scope |
|---|---|---|
| USTA | adobedevxsc/ema-usta |
Improve section (tips, fitness, gear, rules) · nav · footer · metadata |
| USTA Foundation | adobedevxsc/usta-foundation |
Foundation pages · hero sections · impact · news |
Content authoring workflow
Content is created and edited as documents in DA. Authors write blocks using a table syntax — each table row defines a block and its content. When content is previewed or published from the DA sidekick, the EDS pipeline renders the tables into the corresponding block JS/CSS.
.aem.page; "Publish" promotes it to .aem.live.
Query index
The USTA Improve section generates a query index at /en/home/improve/query-index.json (configured in helix-query.yaml). The filter-cards block consumes this index to dynamically render and filter article cards by category. Fields: path, title, description, date, lastModified, featured, author, image, tags.
Blocks reference
The project has 25 total blocks — 16 inherited from the AEM Block Collection and 9 custom blocks built specifically for USTA. Each block lives in its own directory under /blocks/{block-name}/ containing a JS file and a CSS file.
Custom USTA blocks
| Block | Description |
|---|---|
cards-icon | Cards layout with icon images — USTA-specific styling |
columns-feature | Feature columns with background colour/image styling |
columns-stats | Statistics in a column layout with large numerals |
filter-bubble | Filter chip/tag UI — dispatches filter-change events |
filter-cards | Dynamic cards from query-index.json · 6-per-page · listens for filter-change |
hero-landing | Landing page hero variant |
near-you | Placeholder for location-based dynamic content |
player-profile | Tennis player profile display |
player-rankings | Player rankings table (currently mock data) |
Hero variants
The hero block has three body-class-scoped variants that render completely different layouts depending on which site/section the page belongs to:
| Variant | Trigger | Layout |
|---|---|---|
| Default | body .hero | Relative-position background image, white H1, max-width 1200px |
| Foundation | body.foundation .hero | Full-height, dark gradient overlay, left-aligned white text + CTA buttons |
| Improve | body.improve .hero | Banner-style (280px mobile / 340px desktop), teal overlay, centered white H1 |
Developer skills
Highlighted skills and patterns to be productive on this project from day one.
Each block exports a default(block) function. The block element is the decorated DOM node — manipulate its children, query it, decorate it. No framework, no bundler.
filter-bubble dispatches a custom filter-change event on document. filter-cards listens and re-renders. Add new listener blocks using document.addEventListener('filter-change', ...).
A/B testing, audience targeting, and campaigns are handled by the plugin in /plugins/experimentation/. Experiments are configured via metadata in DA documents.
Site theme is set by a body class (body.foundation, body.improve). Block CSS selectors scope to these classes to achieve per-site visual variation from one codebase.
Authors apply section backgrounds via metadata: .light / .highlight (gray), .related-articles (olive/sage), .gold (yellow). These map to CSS classes in styles.css.
No fstab.yaml — DA org configuration handles hostname-to-content-path mapping. Understanding which DA org a URL resolves to is essential for debugging content issues.
Block models for Universal Editor live in /ue/models/blocks/. Run npm run build:json to regenerate root component JSON files after editing any model. The Husky hook does this automatically on commit.
Bulk content migration is handled by Playwright-driven import scripts in /tools/importer/. Four scripts target different source site sections; parsers and transformers handle DOM cleanup.
filter-cards uses a 3-column grid on desktop, 2-column on tablet, 1-column on mobile. Cards paginate in groups of 6 via a "More" button — no infinite scroll.