@ulu/frontend
Version:
A framework-agnostic frontend toolkit providing a modular, tree-shakable library of accessible components and utilities. Designed for seamless integration, it features a highly configurable SCSS system for any environment and vanilla JavaScript modules op
79 lines (56 loc) • 5.33 kB
Markdown
# Development Overview
## System Architecture
The library is designed with a clear separation between styles (SCSS) and behavior (JavaScript). They are loosely coupled, interacting through CSS classes and data attributes in the HTML.
* **SCSS:** Provides the styling layer. It is highly modular, with global settings (color, typography), base element styles, and individual component styles. The file `lib/scss/stylesheets/full.scss` compiles everything into a single, comprehensive stylesheet for easy consumption.
* **JavaScript:** Provides interactivity. It is also modular, with `core` logic, `utility` functions, and `ui` components. The JavaScript is designed to be "tree-shakable," so a project using this library will only bundle the code for the components it actually uses.
### How They Interact
1. **Component Structure:** Components are composed of SCSS and JavaScript files. A component might be SCSS-only (providing styles for a static element), JavaScript-only (providing some background logic), or a pair of both that work together (e.g., `_tabs.scss` and `tabs.js`).
2. **Initialization:** For components with JavaScript, an `init` function (e.g., `tabsInit`) is typically provided. This function scans the DOM for specific elements, usually identified by a `data-ulu-component` attribute.
3. **Styling Agnosticism:** A key design principle is that the JavaScript is styling-agnostic. It identifies and interacts with elements exclusively through `data-ulu-*` attributes, not CSS classes. This intentional separation means you can use the provided JavaScript functionality with completely custom component styling, simply by structuring your HTML with the correct data attributes.
4. **Activation:** When an `init` function finds its corresponding component in the HTML, it instantiates a JavaScript class to manage its state and handle user interactions (like clicks or keyboard events).
5. **State & Styling:** While the JavaScript itself is not tied to the SCSS styling, it does manage state by adding or removing state-based CSS classes (e.g., `.is-active`). The library's SCSS uses these classes to apply styles, and you can use them in your own stylesheets as well.
In short, the SCSS defines what components and their different states *look like*, while the JavaScript is responsible for activating components and changing their states based on user interaction. This creates a flexible and efficient system.
## SCSS Development Conventions
When developing new SCSS components or maintaining existing ones, adhere to the following architectural patterns:
1. **Configuration Property Naming:**
* Modifier-specific properties must begin with the modifier name.
* *Correct:* `clickable-background-color-hover`
* *Incorrect:* `background-color-clickable-hover`
* Responsive state variables should describe the *behavior*, not a hardcoded viewport/breakpoint name if possible. Example a component that stacks when on smaller screens might refer to that breakpoint as `stacked-breakpoint`
2. **No BEM Interpolation with the Parent Selector:**
* Never use the SCSS parent selector `&` to concatenate or interpolate BEM classes (e.g., `&__element` or `&--modifier`).
* The `&` character represents the parent *selector*, not a string. Treating it as a string for concatenation abuses SCSS and makes the codebase harder to search.
* Always write out the full BEM selector or use the module's prefix variable (e.g., `#{ $prefix }__element`).
* *Correct:* `#{ $prefix }__item:not(#{ $prefix }__item--cue)` (Spelling out the full class inside pseudo-classes)
* *Correct:* `&#{ $prefix }__item--cue` (Selector chaining: compiles to `.parent.parent--cue`)
* *Incorrect:* `&:not(&--cue)` or `&--cue` (String concatenation)
3. **Colocated HTML Demos:**
* Every component or significant stylesheet should have a colocated `.demo.html` file (e.g., `_button.scss` -> `_button.demo.html`).
* These files serve as the "Single Source of Truth" for component markup, feeding both the AI Context (MCP server) and the live documentation site.
* Use the `<!-- @ulu-demo ... -->` YAML annotation to define variations.
* Demos should be succinct, high-quality examples of the component's API and states. Omit verbose or redundant examples.
* Use the `wrapperClass` property in the YAML config for any documentation-only styles (e.g., `.demo-theme-box`).
## Documentation
- `site/`: Contains the source code for the documentation website (Eleventy). **Edit files here.**
- `docs/`: The build output for GitHub Pages. **Do not edit files here directly.**
## Benchmark Notes
### Javascript
- Old Vite 4
- No build.target
- /dist/es 183kb (js, types, etc)
- Included aria-tablist dep
- Wasn't minified
- Current Build (Vite 7)
- Javascript Bundle
- Uncompressed: ~74.7 kB
- Gzip: ~30 kB (less when actually bundled with tree-shaking)
- About the size of jquery or a small image
- CSS Bundle
- Uncompressed: ~139 kB
- Gzip: ~21 kB (compresses well)
- In a real project not using every scss module
- Uncompressed: ~80-100 kB
- Gzip: ~13-16 kB
## Naming
Config options, and class names, etc should follow a pattern like:
"[subject]-[property]-[direction]"