Components
Shared components for independent pages.
Directory
Links and explanations form rows. Set the columns to suit the material. Keep the list semantics.
- Site philosophy
The principles behind the shared visual language.
- Component contracts
Markup, ownership, dynamic content, and graceful fallback.
- Authoring skills
Reusable guidance for future pages and shared changes.
Directory markup
<ul class="ui-directory">
<li><a href="guide.html">Guide</a>
<p>A short explanation of what the reader will find.</p>
</li>
</ul>Controls near their results
Native labels and controls wrap together. To filter this example catalogue, use search or select a surface.
Dropdown markup
<label class="ui-field">Surface
<select name="surface" data-ui-select>
<option value="all">All surfaces</option>
<option value="reading">Reading</option>
</select>
</label>The native select supplies the value and form events. The shared menu supports arrow keys, Home, End, typeahead, Enter, and Escape. Without the script, the styled native control remains available.
6 patterns
- Directory
Browse a finite set of documents or projects.
- Toolbar and fields
Keep search, filters, and reset together.
- Scrollable table
Compare columns without shrinking their labels.
- Native dialog
Inspect a record and return to its opener.
- Responsive disclosure
Keep mobile contents and filters in the document.
- Copyable code
Reuse a snippet with clear success or fallback.
No patterns match. Clear the search or choose all surfaces.
Show where the table continues
An indicator appears only when columns extend beyond the viewport. Put keyboard focus in the table region. Then use the arrow keys to scroll. The table keeps its native headings and caption.
| Pattern | Without JavaScript | Keyboard behavior | Layout ownership |
|---|---|---|---|
| Directory | All links are available | Native links | Page-specific columns |
| Responsive disclosure | Contents remains visible | Toggle, Escape, focus return | Page-specific breakpoint |
| Dialog | Native close form works once open | Escape and native focus return | Shared shell; local content |
Table markup
<div class="table-scroll" data-scroll-region aria-label="Comparison">
<table class="ui-table">…caption, headings, and rows…</table>
</div>One record, in context
A native dialog contains keyboard focus and supports Escape. Its close control remains available when long content scrolls.
Dialog markup
<dialog class="ui-dialog" data-dialog aria-labelledby="record-title">
<form method="dialog" class="ui-dialog-dismiss">
<button aria-label="Close details">×</button>
</form>
<div class="ui-dialog-body">
<h2 id="record-title">Record title</h2>
<p>Record content.</p>
</div>
</dialog>Code as code
Highlight source during the build. The palette uses the theme tones, so dark mode and print need no second theme. Long lines scroll inside the listing.
def visible_count(rows, query=""):
"""Count rows whose label contains the query."""
query = query.casefold()
return sum(1 for row in rows if query in row["label"].casefold()) # 0 for no rowsUse only what the page needs
Load the optional component layer after the foundation and before local styles. Explicit data attributes enable its behavior. Pages without these attributes keep their existing behavior.
<script src="https://jehlp.net/site-theme/v2/theme.js?v=20260930-header-home"></script>
<link rel="stylesheet" href="https://jehlp.net/site-theme/v2/base.css?v=20261009-reading-folio">
<link rel="stylesheet" href="https://jehlp.net/site-theme/v2/components.css">
<link rel="stylesheet" href="assets/styles.css">
<script src="https://jehlp.net/site-theme/v2/components.js" defer></script>For components added later, call window.JehlpUI?.enhance(container). Repeated calls are safe. See the full component contracts for examples and limits.