mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-08-20 21:41:43 +10:00
feat: add semantic CSS stylesheets (#3274)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor Agent
parent
4ac19f81b3
commit
d2ffbf9618
@@ -0,0 +1,338 @@
|
||||
---
|
||||
title: "Applying Custom Styles"
|
||||
description: "Use Reactive Resume Semantic CSS to make safe, targeted, and portable changes to your resume PDF."
|
||||
---
|
||||
|
||||
Custom Styles let you make focused changes that are not available in the regular **Design**, **Typography**, **Layout**,
|
||||
**Page**, and **Picture** settings. They use Semantic CSS, a CSS-like language designed for
|
||||
resume PDFs.
|
||||
|
||||
<Note>
|
||||
Semantic CSS styles the PDF output, not the browser interface. It cannot load fonts, images, scripts, or other resources, and
|
||||
it cannot create new resume content.
|
||||
</Note>
|
||||
|
||||
## Convert existing Custom Styles
|
||||
|
||||
If a resume still uses the previous Custom Styles form, Reactive Resume creates a converted stylesheet draft. Your
|
||||
current rules remain active while you review it.
|
||||
|
||||
<Steps>
|
||||
<Step title="Open Custom Styles">
|
||||
Open the resume in the builder, select **Design**, then select **Custom Styles**.
|
||||
</Step>
|
||||
<Step title="Review the converted draft">
|
||||
Check the preview and warnings below the editor. The draft starts with `@version 1;`.
|
||||
</Step>
|
||||
<Step title="Activate Semantic CSS">
|
||||
Select **Activate Semantic CSS** only after the preview matches the legacy result. Reactive Resume never applies both
|
||||
systems at once, and keeps the original legacy rules available for rollback.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Make your first change
|
||||
|
||||
Open the resume you want to style, select **Design**, then select **Custom Styles**. Start with a complete stylesheet:
|
||||
|
||||
```css
|
||||
@version 1;
|
||||
|
||||
section[type="experience"] > section-heading {
|
||||
color: #0f766e;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
```
|
||||
|
||||
The first line tells Reactive Resume which language version the stylesheet uses. Keep `@version 1;` at the start of
|
||||
every stylesheet.
|
||||
|
||||
<Steps>
|
||||
<Step title="Paste one focused rule">
|
||||
Add the stylesheet to the editor. Start with one visual change so it is easy to review in the preview.
|
||||
</Step>
|
||||
<Step title="Wait for Applied">
|
||||
Reactive Resume checks the source and the PDF result. When the status changes to **Applied**, compare the preview
|
||||
and export if you are ready to share the resume.
|
||||
</Step>
|
||||
<Step title="Build on the working rule">
|
||||
Add one related change at a time. The editor keeps your draft, undo history, and last valid stylesheet separately.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Target the right part of your resume
|
||||
|
||||
Semantic CSS selectors describe resume content rather than a template's internal HTML. Selector and attribute names are
|
||||
lowercase and case-sensitive. Prefer semantic selectors when you want a style to work across resumes and templates.
|
||||
|
||||
### Start with the resume structure
|
||||
|
||||
| Selector | Targets | Typical use |
|
||||
| --- | --- | --- |
|
||||
| `resume` | The complete resume | Scope a rule to one template. |
|
||||
| `page` | A rendered PDF page | Set a page size. |
|
||||
| `region` | Header, main, sidebar, or featured region | Style a layout area. |
|
||||
| `header` | The resume header | Style the identity and contact area. |
|
||||
| `section` | A resume section | Target a section type or placement. |
|
||||
| `section-heading` | A section title | Change heading typography or decoration. |
|
||||
| `section-items` | The items in a section | Adjust item layout and gaps. |
|
||||
| `item` | One resume item | Control spacing or pagination for an experience, project, or similar item. |
|
||||
| `item-header` | An item's summary row | Align the title, company, dates, or similar details. |
|
||||
|
||||
### Target header and item content
|
||||
|
||||
| Selector | Targets | Typical use |
|
||||
| --- | --- | --- |
|
||||
| `picture` | The profile picture | Change dimensions, crop, border, or picture shadow. |
|
||||
| `name`, `headline` | Header name and headline | Change the main identity typography. |
|
||||
| `contact-list`, `contact-item` | Header contact details | Space or restyle contact details. |
|
||||
| `combined-text` | A template-combined value | Style an item value that combines fields. |
|
||||
| `field` | A named content field | Target a position, company, date, or other field. |
|
||||
| `link` | A structured link | Change linked text or layout. |
|
||||
| `icon`, `level` | An icon or level indicator | Restyle decorative elements. |
|
||||
|
||||
### Target rich text and lists
|
||||
|
||||
| Selector | Targets |
|
||||
| --- | --- |
|
||||
| `rich-text`, `rich-heading`, `blockquote`, `paragraph` | Rich-text blocks in descriptions and summaries. |
|
||||
| `list`, `list-item`, `list-marker`, `list-item-content` | Lists, the outer item row, its bullet or number, and its content. |
|
||||
| `strong`, `emphasis`, `underline`, `strike`, `code`, `text-span`, `mark` | Inline rich-text formatting. |
|
||||
| `hard-break`, `horizontal-rule` | A forced line break or horizontal rule. |
|
||||
| `template-part` | A template-provided extension point. Use only with a template guard. |
|
||||
|
||||
### Narrow a selector with attributes
|
||||
|
||||
Use attributes to make a rule specific without relying on a template layout.
|
||||
|
||||
| Attribute | Use it with | Example |
|
||||
| --- | --- | --- |
|
||||
| `type` | `section` | `section[type="experience"]` |
|
||||
| `placement` | `region` and `section` | `region[placement="sidebar"]` |
|
||||
| `region` | `region` | `region[region="sidebar"]` |
|
||||
| `origin` | `section` | `section[origin="main"]` |
|
||||
| `part` | `region`, `section`, `contact-item`, and `item-header` | `region[part~="sidebar-background"]` |
|
||||
| `template` | `resume` | `resume[template="azurill"]` |
|
||||
| `name` | `field` and `template-part` | `field[name="position"]` |
|
||||
| `level` | `rich-heading` | `rich-heading[level="2"]` |
|
||||
| `direction` | `list-item-content` | `list-item-content[direction="rtl"]` |
|
||||
| `id` | Any semantic node when present | `section[id="projects"]` |
|
||||
| `role` | Any semantic node when present | `field[role~="secondary-text"]` |
|
||||
|
||||
Semantic CSS supports selector lists, descendant (` `), child (`>`), adjacent sibling (`+`), and general sibling (`~`)
|
||||
combinators. It also supports `:root`, `:first-child`, `:last-child`, `:only-child`, `:is()`, `:where()`, `:not()`,
|
||||
`:nth-child()`, and `:nth-of-type()`.
|
||||
|
||||
```css
|
||||
@version 1;
|
||||
|
||||
section[type="experience"] > section-heading {
|
||||
border-bottom: 1pt solid #0f766e;
|
||||
}
|
||||
|
||||
region[placement="sidebar"] {
|
||||
background-color: #f8fafc;
|
||||
padding: 18pt;
|
||||
}
|
||||
|
||||
section[id="projects"] {
|
||||
break-inside: avoid;
|
||||
}
|
||||
```
|
||||
|
||||
Use an exact `id` only for a resume-specific adjustment. A type, placement, role, or field name is usually a better
|
||||
choice when you expect to copy the stylesheet to another resume.
|
||||
|
||||
## Reuse your builder settings
|
||||
|
||||
Semantic CSS exposes the resolved builder settings as read-only `--resume-*` variables. Define your own variables in `:root`, then
|
||||
reuse the builder values instead of duplicating colors or dimensions.
|
||||
|
||||
```css
|
||||
@version 1;
|
||||
|
||||
:root {
|
||||
--accent: var(--resume-primary-color);
|
||||
--rule: #cbd5e1;
|
||||
}
|
||||
|
||||
section-heading {
|
||||
color: var(--accent);
|
||||
border-bottom: 1pt solid var(--rule);
|
||||
font-size: 11pt;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.4pt;
|
||||
}
|
||||
```
|
||||
|
||||
Changing the primary color or related setting in the builder updates the corresponding variable automatically. Do not
|
||||
assign a value to a `--resume-*` variable; create an author variable such as `--accent` instead.
|
||||
|
||||
| Builder setting | Read-only variables |
|
||||
| --- | --- |
|
||||
| Colors | `--resume-primary-color`, `--resume-text-color`, `--resume-background-color` |
|
||||
| Typography | `--resume-body-font-size`, `--resume-body-line-height`, `--resume-heading-font-size`, `--resume-heading-line-height` |
|
||||
| Page and layout | `--resume-page-gap-x`, `--resume-page-gap-y`, `--resume-page-margin-x`, `--resume-page-margin-y`, `--resume-page-width`, `--resume-page-height`, `--resume-sidebar-width` |
|
||||
| Picture | `--resume-picture-size`, `--resume-picture-rotation`, `--resume-picture-aspect-ratio`, `--resume-picture-border-radius`, `--resume-picture-border-width`, `--resume-picture-border-color`, `--resume-picture-shadow-width`, `--resume-picture-shadow-color` |
|
||||
|
||||
Use `pt` for predictable PDF spacing and type sizes. Semantic CSS also accepts `px`, `in`, `mm`, `cm`, `%`, `vw`, `vh`, `em`,
|
||||
and `rem` where the property supports a length.
|
||||
|
||||
## Style common resume content
|
||||
|
||||
The most useful declarations usually fall into a few groups:
|
||||
|
||||
| Goal | Common declarations |
|
||||
| --- | --- |
|
||||
| Typography | `color`, `font-size`, `font-style`, `font-weight`, `letter-spacing`, `line-height`, `text-align`, `text-decoration`, `text-transform` |
|
||||
| Spacing and layout | `margin`, `padding`, `gap`, `width`, `height`, `display`, `flex`, `flex-direction`, `justify-content`, `align-items`, `order` |
|
||||
| Visual treatment | `background-color`, `border`, `border-radius`, `opacity`, `transform` |
|
||||
| Picture treatment | `object-fit`, `object-position`, `-resume-shadow-color`, `-resume-shadow-width` |
|
||||
| PDF structure | `break-before`, `break-inside`, `orphans`, `widows`, `-resume-min-presence-ahead`, `size` |
|
||||
|
||||
Use `display: none` only to hide an existing semantic node. Semantic CSS cannot add, remove, duplicate, or re-parent resume
|
||||
data.
|
||||
|
||||
### Style rich-text lists
|
||||
|
||||
`list-item` is the outer row that holds a marker and its content. Use it for row layout and spacing. Use `list-marker`
|
||||
for the bullet or number, and `list-item-content` for the text flow.
|
||||
|
||||
```css
|
||||
@version 1;
|
||||
|
||||
rich-text list-item {
|
||||
gap: 4pt;
|
||||
}
|
||||
|
||||
list-marker {
|
||||
color: var(--resume-primary-color);
|
||||
}
|
||||
|
||||
list-item-content {
|
||||
line-height: 1.35;
|
||||
}
|
||||
```
|
||||
|
||||
### Style fields inside an item
|
||||
|
||||
Named fields let you make a focused change without styling every item value. Use the selector only where that field
|
||||
exists in the selected resume and template.
|
||||
|
||||
```css
|
||||
@version 1;
|
||||
|
||||
section[type="experience"] field[name="position"] {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
section[type="experience"] field[name="company"] {
|
||||
color: var(--resume-primary-color);
|
||||
}
|
||||
```
|
||||
|
||||
## Use template-specific parts carefully
|
||||
|
||||
Template parts expose optional visual details that are not shared by every template. Always guard a template-part rule
|
||||
with `resume[template="..."]`; otherwise the selector may match nothing after a template change.
|
||||
|
||||
```css
|
||||
@version 1;
|
||||
|
||||
resume[template="azurill"] template-part[name="timeline-line"] {
|
||||
background-color: #94a3b8;
|
||||
}
|
||||
```
|
||||
|
||||
Some template parts are wrappers, while others are attributes on an existing semantic node. Use the matching selector
|
||||
below.
|
||||
|
||||
| Template | Available selectors |
|
||||
| --- | --- |
|
||||
| Azurill | `template-part[name="timeline-content"]`, `template-part[name="timeline-dot"]`, `template-part[name="timeline-line"]`, `template-part[name="timeline-marker"]` |
|
||||
| Bronzor | `section[part~="interleaved-section-row"]` |
|
||||
| Chikorita | `template-part[name="contact-row-primary"]`, `template-part[name="contact-row-secondary"]` |
|
||||
| Ditgar | `template-part[name="featured-summary"]`, `item-header[part~="item-header-border"]`, `region[part~="sidebar-background"]` |
|
||||
| Ditto | `template-part[name="contact-offset"]`, `template-part[name="header-band"]`, `template-part[name="picture-anchor"]` |
|
||||
| Gengar | `template-part[name="featured-summary"]`, `region[part~="sidebar-background"]` |
|
||||
| Glalie | `region[part~="sidebar-background"]` |
|
||||
| Leafish | `template-part[name="header-body"]`, `template-part[name="header-contact-band"]`, `template-part[name="header-intro"]` |
|
||||
| Meowth | `template-part[name="education-grade-row"]`, `template-part[name="inline-item-header-leading"]`, `template-part[name="inline-item-header-middle"]`, `template-part[name="inline-item-header-trailing"]` |
|
||||
| Pikachu | `template-part[name="header-divider"]` |
|
||||
| Rhyhorn | `template-part[name="contact-item-content"]`, `contact-item[part~="contact-item-last"]` |
|
||||
| Scizor | `template-part[name="header-name-rule"]` |
|
||||
|
||||
Kakuna, Lapras, and Onyx do not expose template-specific parts. Use shared semantic selectors for portable styles.
|
||||
|
||||
## Control pagination and PDF dimensions
|
||||
|
||||
Use structural declarations sparingly and review the exported PDF after each change. You can keep an item together,
|
||||
leave space before a section, or set a custom page size.
|
||||
|
||||
```css
|
||||
@version 1;
|
||||
|
||||
page {
|
||||
size: 210mm 297mm;
|
||||
}
|
||||
|
||||
section {
|
||||
-resume-min-presence-ahead: 72pt;
|
||||
}
|
||||
|
||||
item {
|
||||
break-inside: avoid;
|
||||
}
|
||||
```
|
||||
|
||||
`size` applies only to `page` and must be outside `@media`. PDF media queries use the authored PDF dimensions, not the
|
||||
browser viewport.
|
||||
|
||||
```css
|
||||
@version 1;
|
||||
|
||||
@media (max-width: 600pt) {
|
||||
region[placement="sidebar"] {
|
||||
padding: 12pt;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Supported media features are `width`, `min-width`, `max-width`, `height`, `min-height`, `max-height`, and
|
||||
`orientation: portrait` or `orientation: landscape`.
|
||||
|
||||
## Apply, diagnose, and recover safely
|
||||
|
||||
The editor saves your draft even when it has an error. The preview and PDF export continue using the last stylesheet
|
||||
that compiled and passed PDF checks, so a mistake does not replace a working result.
|
||||
|
||||
If a rule does not work:
|
||||
|
||||
1. Read the status below the editor. Errors include a line and column number when available.
|
||||
2. Check the selector's spelling, attribute value, placement, and template guard. A **selector matches nothing**
|
||||
warning usually means the resume does not contain that semantic node.
|
||||
3. Simplify the rule to one selector and one declaration, then wait for **Applied** before adding more.
|
||||
4. Use **Reset to applied stylesheet** to discard the current draft, or use the stylesheet undo and redo controls to
|
||||
restore an earlier source and applied pair.
|
||||
|
||||
Select **Open focus mode** when you need a taller editor. On mobile, it opens a full-width sheet; switch to
|
||||
**Preview** to inspect the result.
|
||||
|
||||
<Warning>
|
||||
Review the PDF preview before exporting or sharing a resume with Custom Styles. PDF pagination and template-specific
|
||||
details can make a valid stylesheet look different from what you intended.
|
||||
</Warning>
|
||||
|
||||
## Keep styles portable
|
||||
|
||||
When you copy a stylesheet to another resume, semantic section types, placements, roles, and fields are the safest
|
||||
starting point. Exact IDs and template parts are intentionally specific to a resume or template.
|
||||
|
||||
1. Select **Copy stylesheet** in the source resume.
|
||||
2. Open **Design -> Custom Styles** in the destination resume.
|
||||
3. Paste the stylesheet and review any warnings.
|
||||
4. Replace or remove exact IDs and template-part rules that do not apply.
|
||||
5. Wait for **Applied**, then compare the preview and exported PDF.
|
||||
|
||||
Semantic CSS does not support classes, pseudo-elements, CSS Grid, arbitrary at-rules, `@import`, `@font-face`, `url()`,
|
||||
browser APIs, animations, filters, gradients, general box shadows, or external assets. Use the normal builder settings
|
||||
when you need a font, image, or broader layout change.
|
||||
+11
-1
@@ -4,6 +4,16 @@
|
||||
"name": "Reactive Resume",
|
||||
"favicon": "/favicon.svg",
|
||||
"description": "A privacy-minded resume builder that is customizable, portable, open-source, and free to use.",
|
||||
"redirects": [
|
||||
{
|
||||
"source": "/guides/using-custom-styles",
|
||||
"destination": "/applying-custom-styles"
|
||||
},
|
||||
{
|
||||
"source": "/guides/semantic-css-reference",
|
||||
"destination": "/applying-custom-styles"
|
||||
}
|
||||
],
|
||||
"seo": {
|
||||
"indexing": "navigable",
|
||||
"metatags": {
|
||||
@@ -61,7 +71,7 @@
|
||||
"guides/adding-a-cover-letter",
|
||||
"guides/using-the-builder-dock",
|
||||
"guides/undoing-changes-and-version-history",
|
||||
"guides/using-custom-styles",
|
||||
"applying-custom-styles",
|
||||
"guides/using-ai-in-the-builder",
|
||||
"guides/using-ai-agent",
|
||||
"guides/using-private-notes",
|
||||
|
||||
+2078
-374
File diff suppressed because it is too large
Load Diff
@@ -1,337 +0,0 @@
|
||||
---
|
||||
title: "Using Custom Styles"
|
||||
description: "Learn how to use Custom Styles to fine-tune section layouts, text, rich text, lists, links, spacing, borders, and other resume presentation details."
|
||||
---
|
||||
|
||||
Custom Styles let you fine-tune the visual details of your resume after you choose a template. Instead of writing CSS, you create structured style rules that target resume sections and semantic parts of those sections, such as section headings, item containers, normal text, links, rich-text paragraphs, and list rows.
|
||||
|
||||
Use Custom Styles when the regular **Design**, **Typography**, **Layout**, and **Page** settings are too broad. For example, you can make only your Experience headings uppercase, add a border around Projects, tighten the spacing inside rich-text bullet lists, or change how inline links appear in descriptions.
|
||||
|
||||
<Frame caption="Screenshot of the Custom Styles section in the resume builder right sidebar">
|
||||
<img
|
||||
src="/images/guides/using-custom-styles/screenshot-1.webp"
|
||||
alt="Custom Styles section in the right sidebar with target scope, style slot, style controls, and applied rules"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
<Warning>
|
||||
Custom Styles are powerful layout controls. Small changes can improve polish, but large negative margins, heavy
|
||||
borders, or oversized text can make a resume harder to read or cause content to overflow.
|
||||
</Warning>
|
||||
|
||||
## When to use Custom Styles
|
||||
|
||||
Start with the normal builder settings first:
|
||||
|
||||
| Need | Use this first |
|
||||
| --- | --- |
|
||||
| Change the overall color palette | **Design** |
|
||||
| Change body or heading fonts | **Typography** |
|
||||
| Change page size, margins, or section gaps | **Page** |
|
||||
| Move sections between columns or pages | **Layout** |
|
||||
| Hide, reorder, or edit section content | The section controls in the left sidebar |
|
||||
|
||||
Use **Custom Styles** when you need a targeted adjustment, such as:
|
||||
|
||||
- Styling one section differently from the rest of the resume.
|
||||
- Adding padding, background, or border treatment to section items.
|
||||
- Adjusting the spacing between rich-text list bullets and their text.
|
||||
- Making rich-text links, bold text, or highlights stand out.
|
||||
- Tightening rich-text paragraphs or lists in a long section without changing the whole resume.
|
||||
|
||||
## Create a style rule
|
||||
|
||||
<Steps>
|
||||
<Step title="Open your resume in the builder">
|
||||
From the Dashboard, open the resume you want to customize.
|
||||
</Step>
|
||||
|
||||
<Step title="Open the right sidebar">
|
||||
The right sidebar contains the resume-wide presentation controls.
|
||||
</Step>
|
||||
|
||||
<Step title="Open Custom Styles">
|
||||
Select **Custom Styles** from the right sidebar.
|
||||
</Step>
|
||||
|
||||
<Step title="Choose a Target Scope">
|
||||
Choose where the rule should apply: **All sections**, a **Section type**, or a **Specific section**.
|
||||
</Step>
|
||||
|
||||
<Step title="Choose a Style Slot">
|
||||
Choose which part of the target should receive the style, such as **Section heading**, **Item container**, **Primary
|
||||
text**, **Paragraph**, or **List item row**.
|
||||
</Step>
|
||||
|
||||
<Step title="Set the style values">
|
||||
Use the **Color**, **Text**, **Spacing**, and **Border** controls. Empty fields mean "use the template default."
|
||||
</Step>
|
||||
|
||||
<Step title="Review the preview">
|
||||
The resume preview updates as the rule changes. Exported PDFs use the same rendering path as the preview, so the
|
||||
exported PDF should match what you see.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Frame caption="Screenshot of the Target Scope and Style Slot selectors">
|
||||
<img
|
||||
src="/images/guides/using-custom-styles/screenshot-2.webp"
|
||||
alt="Target Scope and Style Slot selectors showing All sections, Section type, Specific section, and grouped style slots"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
## How style rules work
|
||||
|
||||
A Custom Style rule has three parts:
|
||||
|
||||
| Part | What it means | Example |
|
||||
| --- | --- | --- |
|
||||
| **Target Scope** | Where the rule applies | All sections, every Experience section, or one custom Projects section |
|
||||
| **Style Slot** | Which semantic element receives the style | Section heading, Item container, Paragraph, List item row |
|
||||
| **Style values** | The visual properties to apply | Text color, font size, padding, row gap, border width |
|
||||
|
||||
Rules are layered on top of the selected template. The template still provides the base design, and Custom Styles override only the values you set.
|
||||
|
||||
If multiple rules affect the same slot, the more specific rule wins:
|
||||
|
||||
1. **All sections** applies first.
|
||||
2. **Section type** overrides matching All sections values.
|
||||
3. **Specific section** overrides matching Section type and All sections values.
|
||||
|
||||
For example, you can make all section headings green, then make only Experience headings black, then make one specific custom Experience section red.
|
||||
|
||||
<Info>
|
||||
Disabled rules are ignored. Deleted or hidden sections do not render, so their rules have nothing to affect until the
|
||||
section is visible again.
|
||||
</Info>
|
||||
|
||||
## Target scopes
|
||||
|
||||
Target Scope decides how broad a rule should be.
|
||||
|
||||
| Target Scope | What it affects | Useful when |
|
||||
| --- | --- | --- |
|
||||
| **All sections** | Every rendered section where the selected slot exists. This includes built-in sections and custom sections. | You want a resume-wide default, such as all section headings using the same color or all rich-text lists using tighter spacing. |
|
||||
| **Section type** | Every section with that content type. This includes matching custom sections. For example, a Projects-style custom section is affected by a Projects section-type rule. | You want every Experience section, every Skills section, or every Summary-style section to share a treatment. |
|
||||
| **Specific section** | One actual section in this resume. | You have duplicate or custom sections and want only one of them to look different. |
|
||||
|
||||
<Frame caption="Screenshot of selecting a Section type target">
|
||||
<img
|
||||
src="/images/guides/using-custom-styles/screenshot-3.webp"
|
||||
alt="Custom Styles target controls with Section type selected and Experience chosen as the target"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
## Style property groups
|
||||
|
||||
The style editor is grouped by property type. Not every property is meaningful on every slot. Text properties work best on text-facing slots, while spacing, background, and border properties work best on containers.
|
||||
|
||||
<Frame caption="Screenshot of the Color, Text, Spacing, and Border controls">
|
||||
<img
|
||||
src="/images/guides/using-custom-styles/screenshot-4.webp"
|
||||
alt="Custom Styles controls grouped into Color, Text, Spacing, and Border panels"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
### Color
|
||||
|
||||
| Control | What it changes | Notes |
|
||||
| --- | --- | --- |
|
||||
| **Text Color** | Text color on text-facing slots. | Most reliable on heading, text, secondary text, link, and rich-text slots. |
|
||||
| **Background** | Background color behind the selected slot. | Useful on section containers, item containers, paragraphs, list rows, and highlights. |
|
||||
| **Text Decoration Color** | Underline or line-through color. | Use with **Text Decoration**. |
|
||||
| **Opacity** | Transparency of the selected slot. | Values range from 0 to 1. |
|
||||
|
||||
Colors are stored as `rgba(r, g, b, a)` values. Use the color picker when possible.
|
||||
|
||||
### Text
|
||||
|
||||
| Control | What it changes |
|
||||
| --- | --- |
|
||||
| **Font Size** | Size in points. |
|
||||
| **Font Weight** | Weight from 100 to 900. |
|
||||
| **Font Style** | Normal or italic. |
|
||||
| **Line Height** | Line-height multiplier. |
|
||||
| **Letter Spacing** | Space between letters. |
|
||||
| **Text Decoration** | None, underline, or line-through. |
|
||||
| **Decoration Style** | Solid, dashed, or dotted decoration line. |
|
||||
| **Text Align** | Left, center, right, or justify. |
|
||||
| **Text Transform** | None, uppercase, lowercase, or capitalize. |
|
||||
|
||||
Use text controls sparingly. Resume text should stay readable, especially when exported to PDF or parsed by hiring systems.
|
||||
|
||||
### Spacing
|
||||
|
||||
| Control | What it changes | Useful for |
|
||||
| --- | --- | --- |
|
||||
| **Padding** | Space inside the selected slot. | Creating breathing room inside boxes, highlighted paragraphs, or section items. |
|
||||
| **Margin** | Space outside the selected slot. | Moving headings, paragraphs, or items closer together or farther apart. |
|
||||
| **Row Gap** | Vertical gap between children when the selected slot lays out multiple rows. | Increasing or tightening list spacing and stacked item content. |
|
||||
| **Column Gap** | Horizontal gap between children when the selected slot lays out multiple columns or row children. | Increasing or decreasing the space between a bullet marker and bullet text on **List item row**. |
|
||||
|
||||
Spacing values are points. Negative values are allowed for some spacing controls, but they can make content overlap. Prefer small adjustments first.
|
||||
|
||||
### Border
|
||||
|
||||
| Control | What it changes |
|
||||
| --- | --- |
|
||||
| **Border Style** | Solid, dashed, or dotted. |
|
||||
| **Border Width** | Border thickness in points. |
|
||||
| **Border Radius** | Corner roundness in points. |
|
||||
| **Border Color** | Border color. |
|
||||
|
||||
Borders are most useful on container slots such as **Section container**, **Item container**, **Paragraph**, and **List item row**.
|
||||
|
||||
## Style Slots reference
|
||||
|
||||
Style Slots are semantic targets. They describe the part of a section that receives the style.
|
||||
|
||||
### Section slots
|
||||
|
||||
Section slots affect the structured fields of a resume section, such as titles, item headers, dates, keywords, profile links, and level indicators.
|
||||
|
||||
| Style Slot | What it affects | Useful examples |
|
||||
| --- | --- | --- |
|
||||
| **Section container** | The outer wrapper for a section, including the heading and section content. | Add a background tint behind a whole section, add section padding, or place a border around one custom section. |
|
||||
| **Section heading** | The section title, such as Experience, Education, Projects, or a custom section title. | Make all headings uppercase, add extra margin below headings, or use a different color for Skills headings. |
|
||||
| **Item container** | Each item inside a section, such as one job, one school, one project, one skill, or one summary item. | Add padding around each Project, create card-like Education entries, or increase the vertical gap inside Skill items. |
|
||||
| **Primary text** | Normal section text and bold item titles rendered by the template, such as company names, roles, schools, dates, periods, and labels. | Make Experience body text slightly smaller, change date text color in a section type, or align normal text in a custom section. |
|
||||
| **Secondary text** | Smaller supporting text rendered as secondary content, such as skill keywords or interest keywords. | Make skill keywords lighter, reduce keyword font size, or increase opacity for muted metadata. |
|
||||
| **Link** | Structured links outside rich-text descriptions, such as item website links and linked item titles. | Underline project links, change website link color, or make all profile links use the primary color. |
|
||||
| **Icon** | Section-content icons, such as profile, skill, interest, and custom-field icons rendered inside sections. Icon-based level indicators also use the shared icon styling. | Change icon color in Skills, reduce icon opacity in Interests, or use a softer color so icons do not compete with the text. |
|
||||
| **Level indicator** | The wrapper around proficiency indicators used by Skills and Languages. | Add space above level indicators, reduce opacity for less prominent levels, or place a light border around the whole scale. |
|
||||
|
||||
<Info>
|
||||
Custom Styles currently target sections and rich-text content. The resume header, profile picture, name, headline, and
|
||||
contact area are controlled by template, Design, Typography, Page, and Picture settings instead of these section
|
||||
slots.
|
||||
</Info>
|
||||
|
||||
### Rich-text slots
|
||||
|
||||
Rich-text slots affect content entered in rich-text editors, such as Summary content, Experience descriptions, Education descriptions, Project descriptions, Awards, Certifications, Publications, Volunteer, References, cover letters, and summary-style custom sections.
|
||||
|
||||
They do not affect structured fields like company name, school name, date, or website unless those values are inside a rich-text description.
|
||||
|
||||
| Style Slot | What it affects | Useful examples |
|
||||
| --- | --- | --- |
|
||||
| **Paragraph** | Paragraph blocks inside rich-text content. | Tighten long summaries with a smaller line height, add margin between cover letter paragraphs, or add a subtle background behind summary paragraphs. |
|
||||
| **List** | Ordered and unordered list containers inside rich text. | Increase **Row Gap** to add space between bullet items, or reduce **Row Gap** to fit more achievements on a page. |
|
||||
| **List item row** | The outer row for each rich-text list item, including the bullet or number marker and the text content. | Increase **Column Gap** to add more space between the bullet icon and the text, reduce **Column Gap** for compact lists, or add padding/background around each bullet row. |
|
||||
| **List item content** | The text/content area of each rich-text list item after the bullet or number marker. | Change bullet text line height, make only list content smaller, or apply text color without changing the bullet row layout. |
|
||||
| **Inline link** | Links inside rich-text descriptions. This is separate from the **Link** slot used by structured website fields. | Underline links in descriptions, change inline link color, or make links use a dotted underline. |
|
||||
| **Bold text** | Bold or strong text inside rich-text descriptions. | Make bold achievements use the primary color, increase bold font weight, or remove extra emphasis by lowering the weight. |
|
||||
| **Highlight** | Highlighted text inside rich-text descriptions. | Change the default highlight background, make highlighted metrics use a different text color, or reduce highlight opacity. |
|
||||
|
||||
<Tip>
|
||||
**List item row** and **List item content** are intentionally separate. Use **List item row** for layout and chrome,
|
||||
such as padding, background, border, opacity, and the marker-to-text **Column Gap**. Use **List item content** for the
|
||||
bullet text itself, such as color, font size, font weight, line height, text decoration, and text transform.
|
||||
</Tip>
|
||||
|
||||
## Practical examples
|
||||
|
||||
### Increase the space between bullet markers and text
|
||||
|
||||
Use this when bullet text feels too close to the bullet icon or number.
|
||||
|
||||
1. Set **Target Scope** to **All sections** or choose a specific section type, such as **Experience**.
|
||||
2. Set **Style Slot** to **List item row**.
|
||||
3. In **Spacing**, increase **Column Gap**.
|
||||
4. Review the preview and adjust in small increments.
|
||||
|
||||
### Make section headings more distinct
|
||||
|
||||
Use this when your template headings need more contrast.
|
||||
|
||||
1. Set **Target Scope** to **All sections**.
|
||||
2. Set **Style Slot** to **Section heading**.
|
||||
3. Set **Text Color** to your primary brand color.
|
||||
4. Set **Text Transform** to **Uppercase** if you want a stronger heading style.
|
||||
5. Add a small **Margin Bottom** value if headings feel too close to the content.
|
||||
|
||||
### Create card-like project items
|
||||
|
||||
Use this when you want one section to feel visually grouped without changing the whole resume.
|
||||
|
||||
1. Set **Target Scope** to **Specific section**.
|
||||
2. Choose your Projects section.
|
||||
3. Set **Style Slot** to **Item container**.
|
||||
4. Add **Padding** on each side.
|
||||
5. Set a light **Background** color.
|
||||
6. Add **Border Width**, **Border Color**, and a small **Border Radius** if the template supports the look.
|
||||
|
||||
### Tighten long descriptions
|
||||
|
||||
Use this when descriptions or bullet lists take too much vertical space.
|
||||
|
||||
1. Set **Target Scope** to the long section type, such as **Experience**.
|
||||
2. Set **Style Slot** to **Paragraph** and reduce **Line Height** slightly.
|
||||
3. Set **Style Slot** to **List** and reduce **Row Gap**.
|
||||
4. Set **Style Slot** to **List item content** and reduce **Line Height** if bullet text still feels loose.
|
||||
|
||||
<Warning>
|
||||
Avoid reducing line height so far that letters collide or text becomes hard to scan. If the resume still overflows,
|
||||
cut content before making the typography cramped.
|
||||
</Warning>
|
||||
|
||||
### Muting skill keywords
|
||||
|
||||
Use this when skill keywords or interest keywords compete with the main labels.
|
||||
|
||||
1. Set **Target Scope** to **Section type**.
|
||||
2. Choose **Skills** or **Interests**.
|
||||
3. Set **Style Slot** to **Secondary text**.
|
||||
4. Lower **Opacity** or choose a softer **Text Color**.
|
||||
|
||||
## Manage applied rules
|
||||
|
||||
Every rule you create appears in **Applied Rules**. Each rule shows its target, style slot, and a compact summary of the properties you set.
|
||||
|
||||
<Frame caption="Screenshot of the Applied Rules list">
|
||||
<img
|
||||
src="/images/guides/using-custom-styles/screenshot-5.webp"
|
||||
alt="Applied Rules list showing enabled and disabled custom style rules with edit and delete actions"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
Use the rule actions to:
|
||||
|
||||
- **Disable or enable** a rule without deleting it.
|
||||
- **Edit** a rule by loading its target and slot back into the style editor.
|
||||
- **Delete** a rule permanently.
|
||||
- **Reset Style** to remove the rule for the currently selected target and slot.
|
||||
|
||||
<Tip>
|
||||
If a style change looks wrong, disable the rule first. If the resume looks correct again, edit or delete that rule
|
||||
instead of changing unrelated settings.
|
||||
</Tip>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### The style did not change anything
|
||||
|
||||
Check that the selected slot exists in the selected target.
|
||||
|
||||
Common mismatches:
|
||||
|
||||
- Using **Paragraph** for company names or dates. Use **Primary text** instead.
|
||||
- Using **Link** for links inside a description. Use **Inline link** instead.
|
||||
- Using **Secondary text** in a section that does not render secondary text.
|
||||
- Styling **Level indicator** in a section with no skill or language level values.
|
||||
|
||||
### A section-specific rule is overriding my global rule
|
||||
|
||||
This is expected. More specific rules override broader rules for the same property and slot. Check **Applied Rules** for matching Section type or Specific section rules.
|
||||
|
||||
### The resume looks cramped or content overlaps
|
||||
|
||||
Disable the most recent spacing rule and review the preview again. Large negative margins, very small line height, and high border widths are the most common causes.
|
||||
|
||||
### The PDF does not match the preview
|
||||
|
||||
Refresh the builder and export again. The preview and PDF export use the same resume rendering path, so persistent differences usually come from stale preview state or font loading.
|
||||
|
||||
### I want to write custom CSS
|
||||
|
||||
Custom Styles do not accept raw CSS. Reactive Resume renders final resumes through a PDF renderer, so Custom Styles use structured style rules that can be safely translated to PDF styles.
|
||||
+22273
-979
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,667 @@
|
||||
# Semantic CSS Stylesheet Design
|
||||
|
||||
## Status
|
||||
|
||||
Draft for user review. The product behavior in this document has been approved conversationally; the written
|
||||
architecture still requires review before implementation planning.
|
||||
|
||||
## Context
|
||||
|
||||
Reactive Resume renders its templates with React PDF rather than browser HTML. React PDF accepts style objects on a
|
||||
known component tree and supports a broad CSS-like property set, but it does not provide a browser DOM or a general
|
||||
selector engine.
|
||||
|
||||
The current customization system stores constrained rules in `metadata.styleRules`. Each rule targets all sections, a
|
||||
section type, or a section ID and applies an intent to one semantic slot. That design is safe and portable, but its form
|
||||
UI is cumbersome to reproduce or share, and its target model cannot reach headers, individual items or fields, page
|
||||
regions, or template-specific visual parts.
|
||||
|
||||
Semantic CSS replaces the form with a familiar text language. It retains typed compilation and semantic targets rather
|
||||
than promising that arbitrary browser CSS can run inside React PDF.
|
||||
|
||||
## Goals
|
||||
|
||||
- Provide one copy-pastable text stylesheet for all PDF-specific visual customization.
|
||||
- Keep Design, Typography, Layout, Page, and Picture controls as base settings.
|
||||
- Let the stylesheet override those base visuals wherever an exposed semantic PDF node permits it.
|
||||
- Target all sections, groups of section types, one section, one item, one field, structural regions, header content,
|
||||
rich text, and documented template-specific parts.
|
||||
- Support portable theme rules and optional resume-specific rules based on stable IDs.
|
||||
- Support nearly all style properties that the pinned React PDF renderer can safely implement.
|
||||
- Preserve invalid user text while rendering the last valid stylesheet.
|
||||
- Produce identical behavior in browser preview, browser export, public PDF views, and server PDF export.
|
||||
- Convert existing structured style rules without changing their rendered appearance.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- The stylesheet does not edit resume content or mutate builder layout metadata.
|
||||
- The stylesheet does not apply to DOCX or Markdown exports.
|
||||
- It does not expose a browser DOM, JavaScript, arbitrary renderer objects, or executable expressions.
|
||||
- It does not support animations, transitions, interactive pseudo-classes, CSS Grid, generated content, or browser-only
|
||||
properties.
|
||||
- It does not load fonts, images, imports, or any other remote or embedded asset.
|
||||
- Font-family selection remains owned by the Typography section.
|
||||
- Picture source, upload, crop, and visibility data remain owned by the Picture section. The rendered picture node can
|
||||
still be sized, positioned, transformed, or hidden by the stylesheet.
|
||||
|
||||
## Product Model
|
||||
|
||||
The existing visual controls remain the base layer. Semantic CSS is the final author-controlled layer:
|
||||
|
||||
1. Builder visual settings and template defaults.
|
||||
2. Template-specific computed styles.
|
||||
3. Semantic CSS declarations.
|
||||
4. Minimal crash-prevention invariants.
|
||||
|
||||
The stylesheet may visually hide, reorder, resize, or position existing output. These changes affect only PDF
|
||||
presentation. They do not rewrite content, section ordering, page assignments, or other builder data.
|
||||
|
||||
## Persisted Data
|
||||
|
||||
Resume metadata gains a versioned stylesheet value:
|
||||
|
||||
```ts
|
||||
type StylesheetSource = {
|
||||
languageVersion: number;
|
||||
text: string;
|
||||
};
|
||||
|
||||
type SemanticStylesheet = {
|
||||
mode: "legacy" | "semantic";
|
||||
source: StylesheetSource;
|
||||
applied: StylesheetSource;
|
||||
};
|
||||
|
||||
type StylesheetMutationState = {
|
||||
revision: number;
|
||||
stylesheet: SemanticStylesheet;
|
||||
};
|
||||
```
|
||||
|
||||
- `source.text` is the exact editable text and may be invalid.
|
||||
- `applied.text` is the most recent valid text and is the only text used for rendering.
|
||||
- Each value carries its own `languageVersion`, allowing an invalid source written for a future language version to
|
||||
preserve and render an older valid program.
|
||||
- `mode` is the persisted rendering discriminator. A missing stylesheet is interpreted as `legacy`.
|
||||
- `revision` is server-owned concurrency metadata, not resume content. It is stored in a dedicated database column and
|
||||
returned only in the stylesheet mutation envelope.
|
||||
|
||||
The compiled AST or intermediate representation is not persisted. Browser and server compilation is a pure operation
|
||||
cached by language version, source hash, compiler build, semantic registry fingerprint, and PDF adapter fingerprint.
|
||||
Caches are bounded and process-local; they are never treated as durable state.
|
||||
|
||||
Stylesheet state is owned by a dedicated authenticated mutation rather than the existing full-document autosave
|
||||
mutation. It accepts an expected stylesheet revision and resume render-data version. The generic `resume.update` path
|
||||
must preserve the database's stylesheet value instead of replacing it from submitted resume data. This preservation
|
||||
behavior must deploy before clients can send Semantic CSS data.
|
||||
|
||||
Compilation and PDF preflight never run while holding a database lock. The mutation reads an immutable resume snapshot,
|
||||
compiles and preflights against that snapshot, then performs a short transaction that compare-and-swaps both the
|
||||
stylesheet revision and resume render-data version. If either changed, it returns a conflict without writing; the client
|
||||
rebases its unsaved source onto the new snapshot and retries. This prevents promotion against content or base settings
|
||||
that differ from those preflighted.
|
||||
|
||||
The server defines separate state transitions. A source can replace `applied` only after compilation and a bounded PDF
|
||||
render preflight against the current resume succeed:
|
||||
|
||||
- **Edit source:** ignore client-applied text. Store the candidate in `source`. In semantic mode, also store it in
|
||||
`applied` only when compilation and preflight succeed; otherwise preserve the row's current `applied`. In legacy mode,
|
||||
edits remain an inactive draft.
|
||||
- **Activate converted source:** require successful compilation, set `mode` to `semantic`, and store the candidate in
|
||||
both source values after preflight. This requires an explicit **Activate Semantic CSS** action. Merely opening,
|
||||
editing, or autosaving a legacy draft does not activate it.
|
||||
- **Editor undo or redo:** independently compile the historical applied value carried by the local history entry, then
|
||||
preflight it and atomically restore the historical source/applied pair. Reject the transition if the applied value is
|
||||
invalid.
|
||||
- **Import:** compile imported source. If it is invalid, independently validate the imported applied value and retain it
|
||||
only after preflight; otherwise use an empty supported applied source.
|
||||
- **Duplicate:** copy the server-owned stylesheet content while initializing a fresh concurrency revision for the new
|
||||
resume.
|
||||
- **Restore version:** restore the server-owned source/applied pair from the selected snapshot after validating the
|
||||
applied value with its versioned compiler and preflight.
|
||||
|
||||
Every successful transition increments `revision` and returns the canonical state plus diagnostics. Worker jobs and
|
||||
network requests carry the local edit generation and expected revision. The client serializes stylesheet mutations:
|
||||
only one request is in flight, and later edits replace one queued candidate. Every acknowledgement advances the local
|
||||
revision; its source/applied payload updates editor state only when its generation is still current. The queued candidate
|
||||
then submits with the acknowledged revision. Warnings do not block application.
|
||||
|
||||
Concurrency revisions are excluded from JSON export and version snapshots. Import and duplicate initialize a fresh
|
||||
revision; version restore increments the current resume's revision rather than restoring historical concurrency
|
||||
metadata.
|
||||
|
||||
## Compiler Architecture
|
||||
|
||||
The compiler is a universal, environment-neutral package used by the web app, API, and PDF renderer:
|
||||
|
||||
```text
|
||||
source
|
||||
-> CSS tokenizer/parser
|
||||
-> syntax AST
|
||||
-> restricted-language validation
|
||||
-> selector and value compilation
|
||||
-> versioned StyleProgram + diagnostics
|
||||
```
|
||||
|
||||
`StyleProgram` contains normalized selectors, declaration values, source locations, specificity, media conditions, and
|
||||
structural directives. It contains no React or React PDF values. A PDF adapter translates resolved declarations into
|
||||
React PDF styles and primitive props.
|
||||
|
||||
The parser should use a standards-compatible CSS parser rather than a hand-written partial tokenizer. Semantic CSS
|
||||
validation sits on top of that parser and rejects unsupported CSS constructs explicitly.
|
||||
|
||||
Compilation and selector matching must remain deterministic. Diagnostics include severity, code, message, and exact
|
||||
source range.
|
||||
|
||||
Source compilation reports syntax and language-contract diagnostics without needing a resume. A separate semantic
|
||||
analysis pass evaluates a compiled program against the current resume's virtual tree and reports context-dependent
|
||||
warnings such as valid selectors that match no node. Both passes use shared diagnostic types and codes.
|
||||
|
||||
Language versions are positive integers. A compiler implementation for a released version is immutable. Unsupported
|
||||
source versions are preserved as opaque editable text but cannot replace `applied`; rendering continues with the
|
||||
supported applied version or base styles when no supported applied value exists.
|
||||
|
||||
Every compiler version referenced by persisted `applied` data remains available. A compiler can be retired only after a
|
||||
transactional migration recompiles and preflights every affected applied stylesheet with a newer version and no stored
|
||||
resume references the old version.
|
||||
|
||||
## Virtual Semantic Tree
|
||||
|
||||
Selectors match a versioned, immutable virtual resume tree, not React component names:
|
||||
|
||||
```text
|
||||
resume
|
||||
page
|
||||
region
|
||||
header
|
||||
picture
|
||||
name
|
||||
headline
|
||||
contact-list
|
||||
contact-item
|
||||
section
|
||||
section-heading
|
||||
section-items
|
||||
item
|
||||
item-header
|
||||
field
|
||||
link
|
||||
icon
|
||||
level
|
||||
rich-text
|
||||
paragraph
|
||||
list
|
||||
list-item
|
||||
list-marker
|
||||
```
|
||||
|
||||
Template-owned chrome is exposed as `template-part` nodes. Every part name must be registered, documented, and stable.
|
||||
Examples include `timeline-line`, `timeline-dot`, `featured-summary`, `sidebar-background`, and
|
||||
`item-header-border`.
|
||||
|
||||
Each node carries only documented semantic attributes, including the applicable subset of:
|
||||
|
||||
- `id`: stable section or item ID.
|
||||
- `type`: canonical section type.
|
||||
- `name`: field, contact, or template-part name.
|
||||
- `template`: selected template on the root.
|
||||
- `placement`: `main` or `sidebar`.
|
||||
- `region`: `header`, `main`, `sidebar`, `featured`, or another registered region.
|
||||
- `page-number`: one-based layout page number.
|
||||
- `role`: one or more stable roles such as `primary-text`, `secondary-text`, or `structured-link`.
|
||||
|
||||
Custom classes are not supported because resume data has no class-authoring surface. Groups are expressed through
|
||||
selector lists, attributes, `:is()`, and `:where()`.
|
||||
|
||||
All shared primitives and all 15 templates must register their semantic nodes before Semantic CSS becomes the default.
|
||||
Known semantic nodes that are absent from the current template are valid no-ops and produce warnings.
|
||||
|
||||
The normative node contract is:
|
||||
|
||||
```ts
|
||||
type SemanticNode = {
|
||||
key: string;
|
||||
kind: SemanticNodeKind;
|
||||
id?: string;
|
||||
attributes: Readonly<Record<string, string>>;
|
||||
roles: readonly string[];
|
||||
children: readonly SemanticNode[];
|
||||
};
|
||||
```
|
||||
|
||||
Each template builds one authoritative descriptor tree from `ResumeData`, template configuration, normalized rich-text
|
||||
content, and the typed semantic registries. Selector matching, context-dependent diagnostics, inheritance, structural
|
||||
resolution, and React rendering all consume that same tree. React components must not create unregistered semantic
|
||||
children independently.
|
||||
|
||||
The registries normatively define allowed parentage, cardinality, field names, role names, stable keys, and
|
||||
template-part placement. Experience roles, custom fields, rich-text nodes, featured summaries, and template-specific
|
||||
header structures are explicitly represented rather than inferred from React children.
|
||||
|
||||
## Selector Language
|
||||
|
||||
Semantic CSS supports:
|
||||
|
||||
- Type selectors and the universal selector.
|
||||
- ID and attribute selectors.
|
||||
- Selector lists separated by commas.
|
||||
- Descendant, child, adjacent-sibling, and general-sibling combinators.
|
||||
- `:is()`, `:where()`, and `:not()`.
|
||||
- Static structural pseudo-classes such as `:first-child`, `:last-child`, `:only-child`, `:nth-child()`, and
|
||||
`:nth-of-type()`.
|
||||
|
||||
Interactive or browser-state pseudo-classes are errors.
|
||||
|
||||
`SemanticNode.id` is reflected to both `#id` and `[id="…"]`. `roles` is reflected as a space-separated `role`
|
||||
attribute and matched with `[role~="token"]`. Other entries in `attributes` are exposed by their registered names.
|
||||
Presence, `=`, `~=`, `|=`, `^=`, `$=`, and `*=` attribute operators are supported. Semantic element, attribute, role,
|
||||
and registered keyword names are lowercase and ASCII case-sensitive. Values and IDs are case-sensitive. Selectors use
|
||||
standard CSS escaping; quoted `[id="…"]` is the recommended syntax for UUIDs that would require identifier escapes.
|
||||
|
||||
Examples:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--accent: #2563eb;
|
||||
--compact-gap: 4pt;
|
||||
}
|
||||
|
||||
section:is([type="experience"], [type="education"]) {
|
||||
margin-bottom: 8pt;
|
||||
}
|
||||
|
||||
section#experience > section-heading {
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
region[placement="sidebar"] section,
|
||||
section#skills {
|
||||
background-color: rgba(20, 30, 40, 0.08);
|
||||
}
|
||||
|
||||
item[id="f27be2d2-13a9-4f16-8248-c8735a27dd1c"] field[name="period"] {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
resume[template="azurill"] template-part[name="timeline-dot"] {
|
||||
background-color: var(--accent);
|
||||
}
|
||||
```
|
||||
|
||||
Portable styles should prefer section types, roles, placements, regions, and template attributes. Exact section and item
|
||||
IDs are available when a rule intentionally belongs to one resume.
|
||||
|
||||
## Cascade and Inheritance
|
||||
|
||||
Semantic CSS follows familiar author-style cascade rules:
|
||||
|
||||
- `!important` declarations outrank normal declarations.
|
||||
- Specificity compares IDs, then attributes and pseudo-classes, then element names.
|
||||
- `:where()` contributes zero specificity.
|
||||
- Equal specificity is resolved by source order.
|
||||
- Custom properties cascade and inherit.
|
||||
- Cyclic or unresolved variables are errors unless a valid fallback exists.
|
||||
|
||||
Only properties marked inheritable in the property registry inherit through the semantic tree. Box and layout
|
||||
properties never inherit implicitly. The language supports `inherit`, `initial`, `unset`, and `revert`; `revert`
|
||||
removes the winning Semantic CSS declaration at that node and exposes its builder/template base value. If the property
|
||||
is inheritable and the semantic parent has a computed Semantic CSS value, normal inheritance can still supply that
|
||||
parent value. `initial` uses the property registry's initial value, `inherit` uses the semantic parent's computed value,
|
||||
and `unset` chooses `inherit` for inheritable properties and `initial` otherwise. `revert-layer` is unsupported.
|
||||
|
||||
Declarations are resolved after template styles. Existing cosmetic safety defaults such as text shrinking must move
|
||||
below the stylesheet in precedence. Only constraints required to prevent renderer failure may remain above user
|
||||
declarations, and each such constraint must be documented.
|
||||
|
||||
Resolution uses one immutable source-tree snapshot:
|
||||
|
||||
1. Match all selectors against original parentage and sibling order.
|
||||
2. Calculate selector specificity according to CSS rules: `:is()` and `:not()` take their most specific argument,
|
||||
while `:where()` has zero specificity.
|
||||
3. Cascade declarations and custom properties, then calculate inherited values.
|
||||
4. Resolve structural declarations once.
|
||||
5. Omit `display: none` subtrees and stable-sort remaining siblings by `order`, using original sibling order for ties.
|
||||
6. Render the resolved tree.
|
||||
|
||||
Hidden and reordered nodes never change which selectors match, positional pseudo-classes, sibling combinators, or
|
||||
inheritance. Structural declarations cannot trigger a second selector pass.
|
||||
|
||||
## Properties, Values, and Units
|
||||
|
||||
The property registry exposes the applicable React PDF surface under familiar kebab-case names:
|
||||
|
||||
- Flexbox layout, including gaps and `order`.
|
||||
- Width, height, minimum and maximum dimensions.
|
||||
- Relative and absolute positioning, overflow, stacking, and display.
|
||||
- Color, background color, and opacity.
|
||||
- Text size, weight, style, line height, spacing, alignment, decoration, transform, indentation, overflow, and line
|
||||
limits.
|
||||
- Margins, padding, borders, radii, and supported transforms.
|
||||
- Supported image sizing and object-fit behavior on the existing picture node.
|
||||
|
||||
`font-family` is rejected. Asset-bearing properties and functions such as `background-image`, `src`, and `url()` are
|
||||
rejected.
|
||||
|
||||
Common shorthands such as `margin`, `padding`, `border`, `gap`, `flex`, and `transform` compile into normalized values.
|
||||
Supported units are `pt`, `in`, `mm`, `cm`, `%`, `vw`, `vh`, `em`, and `rem`. Unitless PDF dimensions are interpreted
|
||||
as points. `px` is accepted for familiarity and converted from 96 DPI to 72-DPI PDF points.
|
||||
|
||||
`rem` resolves against the root body font size from Typography. For `font-size`, `em` resolves against the semantic
|
||||
parent's computed font size. For all other properties, it resolves against the target node's computed font size.
|
||||
Relative-unit cycles are errors.
|
||||
|
||||
Media queries use standard syntax and support page width, page height, and orientation:
|
||||
|
||||
```css
|
||||
@media (max-width: 500pt) {
|
||||
region[placement="sidebar"] {
|
||||
width: 30%;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Page and pagination behavior uses standard properties where possible and namespaced extensions where React PDF exposes
|
||||
primitive props rather than style properties:
|
||||
|
||||
```css
|
||||
section[type="experience"] {
|
||||
break-inside: avoid;
|
||||
-resume-min-presence-ahead: 24pt;
|
||||
}
|
||||
|
||||
page {
|
||||
size: A4;
|
||||
}
|
||||
|
||||
header {
|
||||
-resume-fixed: true;
|
||||
}
|
||||
```
|
||||
|
||||
Supported structural declarations include:
|
||||
|
||||
- `display: none` to omit a semantic node.
|
||||
- `order` to reorder siblings before React rendering.
|
||||
- `break-before: page`.
|
||||
- `break-inside: avoid`.
|
||||
- `orphans` and `widows`.
|
||||
- `-resume-fixed`.
|
||||
- `-resume-min-presence-ahead`.
|
||||
- `size` on page nodes.
|
||||
|
||||
Structural declarations are resolved while preparing semantic child descriptors, before the React component tree is
|
||||
created. CSS cannot move a node to a different parent; absolute positioning can only change its visual placement.
|
||||
|
||||
`page-number` identifies the one-based authored `metadata.layout.pages` entry. React PDF may wrap one authored page into
|
||||
multiple physical subpages; those physical subpages are not independently selectable. They inherit the authored page
|
||||
context, and fixed nodes repeat on physical subpages created from that authored page.
|
||||
|
||||
Page sizing is evaluated in a non-circular phase. Non-media `size` declarations resolve first against builder defaults.
|
||||
Media conditions then evaluate against that final authored page size. `size` inside `@media` is an error.
|
||||
|
||||
Values must be finite. Very large, negative, or overlap-prone values produce warnings rather than cosmetic clamping.
|
||||
Hard technical limits exist only to prevent crashes, pathological allocations, or denial of service.
|
||||
|
||||
## Editor Experience
|
||||
|
||||
The Custom Styles right-sidebar section becomes a monospaced stylesheet editor. It also offers an expanded mode with
|
||||
more editing space while retaining the live preview.
|
||||
|
||||
Editor capabilities include:
|
||||
|
||||
- CSS syntax highlighting.
|
||||
- Line and column diagnostics with error and warning severity.
|
||||
- Selector, attribute, property, keyword, and variable completion.
|
||||
- Hover documentation generated from semantic and property registries.
|
||||
- Color previews.
|
||||
- Search and replace.
|
||||
- Explicit formatting.
|
||||
- Standard copy and paste.
|
||||
- A clear applied state.
|
||||
|
||||
The editor preserves source text and formatting exactly unless the user explicitly formats it.
|
||||
|
||||
Compilation runs after a short debounce in a web worker. The status must distinguish:
|
||||
|
||||
- `Applied`.
|
||||
- Applied with warnings.
|
||||
- Errors, with an explicit message that preview and export use the last valid version.
|
||||
|
||||
The editor maintains source state separately from full-resume autosave. It runs a browser render preflight for a
|
||||
compiled candidate and sends serialized, debounced, revisioned stylesheet mutations. It always consumes response
|
||||
revisions, but replaces visible source/applied state only for the current edit generation. Existing coalesced undo and
|
||||
redo behavior includes both stylesheet values and uses the explicit restore transition, so undo restores matching text
|
||||
and rendered output.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
Errors prevent a new source from becoming applied:
|
||||
|
||||
- Invalid CSS syntax.
|
||||
- Unknown semantic element or attribute.
|
||||
- Unknown or unsupported property.
|
||||
- Invalid value, unit, selector, pseudo-class, at-rule, or variable cycle.
|
||||
- Disallowed font or asset access.
|
||||
- Exceeded source, rule, nesting, or selector-complexity limit.
|
||||
|
||||
Warnings do not prevent application:
|
||||
|
||||
- A known selector matches no node in the current resume or template.
|
||||
- A property is valid but ineffective on the selected semantic node.
|
||||
- An extreme value is likely to cause overlap, clipping, or unreadable output.
|
||||
|
||||
The server returns compiler diagnostics for save responses. Browser diagnostics remain immediate and use the same
|
||||
compiler, semantic analyzer, and diagnostic codes.
|
||||
|
||||
Editable source, source locations, comments, and diagnostics are owner-only data. Public resume responses exclude both
|
||||
stylesheet source values. They contain a fully resolved projection:
|
||||
|
||||
```ts
|
||||
type PublicStyleProjection = {
|
||||
formatVersion: 1;
|
||||
languageVersion: number;
|
||||
semanticTreeVersion: number;
|
||||
registryFingerprint: string;
|
||||
adapterFingerprint: string;
|
||||
renderDataHash: string;
|
||||
nodes: Readonly<Record<string, ResolvedPdfNodeStyle>>;
|
||||
};
|
||||
```
|
||||
|
||||
The server builds this projection from the applied program and authoritative semantic tree. It contains final
|
||||
declarations and structural props keyed by stable node key, with variables already resolved and comments, variable
|
||||
names, selectors, source spans, and diagnostics removed. The public browser accepts it only when all versions,
|
||||
fingerprints, and render-data hash match.
|
||||
|
||||
`renderDataHash` is SHA-256 over a domain-separated, RFC 8785 JSON Canonicalization Scheme serialization of the complete
|
||||
public render input and resolved node projection. The domain includes the projection format version. It excludes
|
||||
owner-only metadata and both stylesheet source values. The browser recomputes the hash before accepting the projection.
|
||||
On mismatch it requests a fresh projection or falls back to the server-rendered PDF. That fallback uses the existing
|
||||
public-resume visibility/password policy and public rendering rate limits; it is not an authorization bypass. Server PDF
|
||||
export compiles the database's applied value directly.
|
||||
|
||||
## Legacy Migration
|
||||
|
||||
`metadata.styleRules` remains readable during compatibility rollout.
|
||||
|
||||
If a resume has legacy rules but no active Semantic CSS value:
|
||||
|
||||
1. Existing PDF rendering continues to use legacy rules.
|
||||
2. Opening Custom Styles deterministically converts the rules into Semantic CSS.
|
||||
3. The generated source preserves target specificity and array order.
|
||||
4. Camel-case intent properties become kebab-case CSS declarations.
|
||||
5. Numeric dimensions become explicit point values.
|
||||
6. Rule labels become comments.
|
||||
7. Disabled rules become clearly labeled commented blocks.
|
||||
8. Draft autosave keeps legacy rendering active.
|
||||
9. The user compares the converted preview and explicitly selects **Activate Semantic CSS**; active stylesheet
|
||||
rendering then takes precedence.
|
||||
|
||||
Legacy target and slot mappings compile to equivalent semantic selectors and roles. For example:
|
||||
|
||||
```css
|
||||
/* Experience heading */
|
||||
section[type="experience"] > section-heading {
|
||||
font-size: 20pt;
|
||||
}
|
||||
```
|
||||
|
||||
Conversion is behavioral rather than a blind property rename. It evaluates each rule through the legacy resolver,
|
||||
including specificity, numeric clamps, link-decoration ordering, bold/template precedence, icon-size translation, and
|
||||
known template exceptions. The serializer emits the effective stylesheet deltas needed to preserve the current
|
||||
resume's rendered appearance. It retains portable original scopes where behavior is equivalent and emits
|
||||
resume-specific role or ID exceptions where legacy composition requires them.
|
||||
|
||||
Labels, IDs, attribute values, comments, strings, and comment terminators are escaped through one CSS serializer. Legacy
|
||||
declarations that had no rendered effect remain non-applying and are explained in generated comments rather than
|
||||
silently gaining new behavior.
|
||||
|
||||
Visual parity is guaranteed at activation for the current resume data, template, and builder base settings. Subsequent
|
||||
template or base-setting changes follow Semantic CSS behavior; they are not guaranteed to reproduce how the retired
|
||||
legacy resolver would have reacted.
|
||||
|
||||
Legacy rules remain as read-only rollback data during the flagged compatibility phase. Old Reactive Resume JSON imports
|
||||
continue to parse them. New exports include the complete versioned stylesheet value. Copying from the editor copies only
|
||||
the editable `source`.
|
||||
|
||||
No bulk database migration is required.
|
||||
|
||||
The server-owned stylesheet revision requires a normal DDL migration that adds a revision column with a zero default.
|
||||
The statement above means no bulk backfill or rewrite of existing resume JSONB rows is required.
|
||||
|
||||
## Security and Resource Limits
|
||||
|
||||
Semantic CSS is declarative and cannot execute code or fetch resources.
|
||||
|
||||
The compiler enforces bounded:
|
||||
|
||||
- Source length.
|
||||
- Rule and declaration count.
|
||||
- Selector length and combinator count.
|
||||
- Functional pseudo-class nesting.
|
||||
- Variable expansion depth.
|
||||
- Media-query nesting.
|
||||
|
||||
Compiler caches are bounded by count and total memory. Browser compilation runs in a worker. Server compilation uses the
|
||||
same limits before rendering or persistence. Unsupported language versions are rejected explicitly rather than silently
|
||||
interpreted by a newer grammar.
|
||||
|
||||
The renderer-versioned property registry defines every property's value grammar, shorthand expansion, inheritance,
|
||||
allowed primitive kinds, relative-unit behavior, and hard technical bounds. Validation runs again after variable and
|
||||
shorthand expansion, so banned asset functions cannot be hidden inside either construct.
|
||||
|
||||
PDF generation additionally enforces maximum authored page dimensions, maximum output pages, render timeout, and memory
|
||||
budgets. Candidate promotion performs this bounded render preflight before replacing `applied`. A preflight failure
|
||||
saves the editable source, preserves the previous applied value, and returns a controlled diagnostic. Later renderer
|
||||
failures caused by subsequent content changes return a controlled preview/export error but do not silently mutate
|
||||
stylesheet history.
|
||||
|
||||
## Documentation Registry
|
||||
|
||||
Semantic element names, attributes, template-part names, properties, values, inheritance behavior, and supported node
|
||||
types come from typed registries. The editor completion data, user documentation, compiler validation, and template
|
||||
coverage tests are generated from these registries.
|
||||
|
||||
This makes undocumented template internals unreachable and prevents documentation from drifting away from runtime
|
||||
behavior.
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Compiler
|
||||
|
||||
- Golden lexer and parser fixtures for valid and invalid source.
|
||||
- Selector matching, specificity, source order, `!important`, inheritance, variables, resets, shorthands, units, and
|
||||
media queries.
|
||||
- Structural directive resolution.
|
||||
- Exact source-range diagnostics.
|
||||
- Property-registry exhaustiveness against supported PDF adapter types.
|
||||
- Fuzz and resource-limit tests proving malformed text cannot crash or hang compilation.
|
||||
|
||||
### Schema and persistence
|
||||
|
||||
- Revision compare-and-swap rejects stale concurrent saves.
|
||||
- Preflight occurs outside database locks, followed by a short CAS on both stylesheet revision and resume render-data
|
||||
version.
|
||||
- Serialized mutations consume stale acknowledgements for revision advancement without replacing newer editor state.
|
||||
- Out-of-order worker results cannot replace newer editor state.
|
||||
- Valid source edits replace both stylesheet values.
|
||||
- Invalid source edits are stored while the current applied value is preserved.
|
||||
- Compile-valid but render-failing source is stored without replacing the current applied value.
|
||||
- Editor undo/redo restores historical invalid source with its historical valid applied value.
|
||||
- Generic full-resume updates preserve the server-owned stylesheet.
|
||||
- Clients cannot forge `applied` through normal edit transitions.
|
||||
- Imports with invalid source retain text and independently validate the imported applied value.
|
||||
- Duplicate and version restore preserve valid source/applied pairs.
|
||||
- Public DTOs redact source, comments, diagnostics, and source locations.
|
||||
- Public projections reject registry, tree, adapter, or render-data-hash mismatches and use the defined fallback.
|
||||
- Public render hashes use the canonical, domain-separated contract, and fallback rendering preserves public/password
|
||||
authorization and rate limiting.
|
||||
- Backend-first rolling deployment preserves stylesheet fields when old clients submit full resume data.
|
||||
- Undo, redo, JSON import, JSON export, duplication, and version restore preserve stylesheet state.
|
||||
- Legacy conversion preserves effective output across precedence quirks, clamps, template exceptions, and supported
|
||||
intent properties.
|
||||
|
||||
### PDF rendering
|
||||
|
||||
- Shared semantic primitives receive correct ancestry and attributes.
|
||||
- Header, picture, contacts, pages, regions, sections, items, fields, rich text, and template parts resolve styles.
|
||||
- Structural hiding and ordering occur before rendering.
|
||||
- Positional selectors and inheritance remain based on the immutable source tree after hiding and ordering.
|
||||
- Authored-page selectors, wrapped physical subpages, fixed nodes, page size, and media queries follow the defined phase
|
||||
model.
|
||||
- Browser and server adapters resolve identical programs.
|
||||
- Every template smoke-renders with a comprehensive stylesheet.
|
||||
- Every registered node and template part has resolved-style coverage.
|
||||
- All 15 templates have visual regression coverage; focused fixtures cover every unique template feature.
|
||||
- Preview and exported PDF use the same applied stylesheet value.
|
||||
|
||||
### Web editor
|
||||
|
||||
- Diagnostics, completions, formatting, search, copy and paste, color previews, autosave, and expanded mode.
|
||||
- Invalid edits preserve source and last-valid preview.
|
||||
- Correcting invalid text applies it without losing formatting.
|
||||
- Out-of-order compilation and save responses are discarded.
|
||||
- Stale save acknowledgements still advance the mutation revision before the queued edit is sent.
|
||||
- Revision conflicts rebase the editor without dropping unsaved source.
|
||||
- Known-but-absent selectors produce warnings.
|
||||
- Legacy conversion is deterministic and user-visible.
|
||||
|
||||
### End-to-end acceptance
|
||||
|
||||
One portable stylesheet is pasted into resumes using different templates. The test verifies group selectors, one
|
||||
section-specific rule, one item-specific rule, a header rule, a rich-text rule, a template-part rule, a media query, and
|
||||
a pagination directive. It then introduces an error, confirms that preview and export remain on the last valid version,
|
||||
corrects the error, and confirms that preview and export update together.
|
||||
|
||||
## Rollout
|
||||
|
||||
1. Deploy the dormant compiler and registries, tolerant schema handling, public projection/redaction,
|
||||
generic-update field preservation, and the dedicated revisioned stylesheet mutation to the entire backend fleet.
|
||||
No client can activate Semantic CSS during this stage.
|
||||
2. Introduce the legacy converter behind a disabled authoring feature flag.
|
||||
3. Instrument shared PDF primitives and structural child preparation.
|
||||
4. Instrument header and template-specific parts across all 15 templates.
|
||||
5. Add the editor and revision/conflict behavior.
|
||||
6. Run legacy and Semantic CSS rendering paths side by side in tests, without double-applying them.
|
||||
7. Enable Semantic CSS for opted-in resumes while retaining legacy rollback data and monitoring compile failures,
|
||||
revision conflicts, render latency, memory, output pages, and fallback usage.
|
||||
8. Enable it by default after mixed-client compatibility, public-redaction, template coverage, visual regression,
|
||||
resource-limit, and end-to-end gates pass.
|
||||
|
||||
The authoring flag controls editor availability and whether a rollout cohort creates new resumes in semantic mode.
|
||||
Before default enablement, resumes outside that cohort start in legacy mode; after default enablement they start in
|
||||
semantic mode with empty version-1 source values. Rendering always honors a persisted semantic mode even if authoring is
|
||||
later disabled. A stylesheet is never applied on top of legacy rules; an active stylesheet takes sole precedence for
|
||||
custom PDF styling.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- Users can copy one text block between resumes and reproduce portable PDF styling.
|
||||
- Every documented semantic node and template part can be targeted consistently.
|
||||
- One section or item can be targeted by stable ID without making portable selectors resume-specific.
|
||||
- Invalid text is never lost and never breaks preview or export.
|
||||
- Preview, public rendering, browser export, and server export agree.
|
||||
- Existing custom styles retain visual parity after deterministic conversion.
|
||||
- The system accepts no executable code, font choice, asset reference, or network-fetching construct.
|
||||
- All 15 templates pass semantic coverage and PDF smoke tests.
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
# Semantic CSS Author Reference and Unified Documentation Generation
|
||||
|
||||
**Date:** 2026-07-29
|
||||
**Status:** Approved
|
||||
|
||||
## Summary
|
||||
|
||||
Reactive Resume will provide one canonical, author-facing Semantic CSS reference at:
|
||||
|
||||
`https://docs.rxresu.me/guides/semantic-css-reference`
|
||||
|
||||
The existing `docs/guides/semantic-css-reference.mdx` page will be expanded rather than duplicated. It will combine
|
||||
hand-written explanations and copy-paste examples with generated tables sourced from the runtime registries and PDF
|
||||
template manifests.
|
||||
|
||||
The Custom Styles editor will include a compact, accessible help hint linking directly to that page.
|
||||
|
||||
A new root command, `pnpm docs:gen`, will replace `pnpm docs:semantic-css` and regenerate:
|
||||
|
||||
1. Semantic CSS reference tables.
|
||||
2. The resume-builder skill schema reference.
|
||||
3. The complete JSON Schema embedded in the public schema guide.
|
||||
4. The checked-in OpenAPI specification.
|
||||
|
||||
## Audience and goals
|
||||
|
||||
The reference is for resume authors who write Semantic CSS in the builder. It must let an author:
|
||||
|
||||
- Discover what selectors, properties, values, and directives exist.
|
||||
- Understand which semantic nodes and template parts can be targeted.
|
||||
- Copy working examples for common customizations.
|
||||
- Diagnose invalid or ineffective styles.
|
||||
- Understand portability, last-valid behavior, resource limits, and unsupported syntax.
|
||||
|
||||
The page is a language reference, not contributor documentation. Compiler architecture, AST implementation details,
|
||||
internal adapter names, and package ownership stay out of the public page.
|
||||
|
||||
## Canonical page structure
|
||||
|
||||
The reference is organized for lookup rather than linear reading.
|
||||
|
||||
### 1. Semantic CSS in one minute
|
||||
|
||||
- The `@version 1;` directive.
|
||||
- One complete, portable stylesheet.
|
||||
- The relationship between editable source, applied source, preview, and export.
|
||||
|
||||
### 2. Selector grammar
|
||||
|
||||
- Universal, semantic type, ID, and attribute selectors.
|
||||
- Supported attribute operators.
|
||||
- Descendant, child, adjacent-sibling, and general-sibling combinators.
|
||||
- Selector lists.
|
||||
- Supported functional and structural pseudo-classes.
|
||||
- Case-sensitivity behavior.
|
||||
- Explicitly unsupported selector syntax.
|
||||
- Paired valid and invalid examples.
|
||||
|
||||
### 3. Semantic element catalog
|
||||
|
||||
- Generated parent and child relationships.
|
||||
- Generated attributes and roles.
|
||||
- Known attribute value domains.
|
||||
- Portable section-type selectors versus resume-specific IDs.
|
||||
- Rich-text structure, including distinct list-item row and list-item content semantics.
|
||||
|
||||
### 4. Cascade and values
|
||||
|
||||
- Specificity, source order, selector-list specificity, inheritance, and `!important`.
|
||||
- Semantic CSS behavior for `initial`, `inherit`, `unset`, and `revert`.
|
||||
- Author custom properties, nested `var()` fallbacks, unresolved variables, and cycles.
|
||||
- Reserved read-only `--resume-*` system variables.
|
||||
- Numbers, lengths, units, colors, functions, and shorthands.
|
||||
|
||||
### 5. Property reference
|
||||
|
||||
- Generated property table grouped by category.
|
||||
- Applicability by semantic node.
|
||||
- Inheritance.
|
||||
- Accepted units and constrained keywords where authoritative metadata exists.
|
||||
- Examples for text, spacing, borders, flex layout, images, transforms, and structural properties.
|
||||
|
||||
The generated table must not present a loose registry hint as an exhaustive value grammar. Value syntax that is
|
||||
implemented by parser or cascade logic remains hand-written unless it has authoritative shared metadata.
|
||||
|
||||
### 6. PDF behavior
|
||||
|
||||
- Page sizing.
|
||||
- Hiding and stable sibling ordering.
|
||||
- Pagination, fixed content, minimum presence ahead, orphans, and widows.
|
||||
- Media-query grammar, evaluation order, and page-dimension behavior.
|
||||
- React PDF-specific layout limitations that affect authors.
|
||||
|
||||
### 7. Template-specific selectors
|
||||
|
||||
- A generated matrix for all 15 templates.
|
||||
- Exact template-part names.
|
||||
- Selector forms.
|
||||
- Owner or placement conditions.
|
||||
- Allowed semantic children.
|
||||
- Portability warnings and guarded selector examples.
|
||||
|
||||
The matrix is generated from actual template manifests, not an independently maintained list.
|
||||
|
||||
### 8. Diagnostics and limits
|
||||
|
||||
- Stable compiler and preflight diagnostic codes.
|
||||
- Severity.
|
||||
- Meaning and likely corrective action.
|
||||
- Source, selector, declaration, node, page, size, timeout, and memory limits.
|
||||
- Last-valid preview and export behavior after an invalid edit.
|
||||
|
||||
### 9. Copy-paste recipes
|
||||
|
||||
- Restyle section headings.
|
||||
- Target a section type.
|
||||
- Target one section, item, or field.
|
||||
- Style sidebar content by placement.
|
||||
- Customize rich-text lists.
|
||||
- Change authored page dimensions.
|
||||
- Prevent awkward page breaks.
|
||||
- Customize optional template decoration.
|
||||
- Apply dimension-dependent PDF styles with `@media`.
|
||||
|
||||
### 10. Unsupported capabilities and portability checklist
|
||||
|
||||
- Unsupported selector, at-rule, layout, asset, font, script, interaction, and network capabilities.
|
||||
- Guidance for keeping a stylesheet portable across templates.
|
||||
|
||||
## Generated documentation architecture
|
||||
|
||||
### Command
|
||||
|
||||
The root package exposes:
|
||||
|
||||
```bash
|
||||
pnpm docs:gen
|
||||
```
|
||||
|
||||
The existing `docs:semantic-css` command is replaced by `docs:gen`, leaving one canonical documentation-generation
|
||||
entrypoint.
|
||||
|
||||
### Semantic CSS reference data
|
||||
|
||||
Generated Semantic CSS sections consume existing authoritative sources:
|
||||
|
||||
- Supported versions and compile limits.
|
||||
- Semantic element registry.
|
||||
- Property registry.
|
||||
- Read-only system-variable registry.
|
||||
- PDF template manifests.
|
||||
- Shared compiler and preflight diagnostic catalogs.
|
||||
|
||||
The generator emits deterministic, marker-delimited sections into
|
||||
`docs/guides/semantic-css-reference.mdx`.
|
||||
|
||||
Generated factual sections include:
|
||||
|
||||
- Semantic elements, parents, attributes, roles, and known value domains.
|
||||
- Property category, applicability, inheritance, units, and constrained keywords.
|
||||
- System variables.
|
||||
- Per-template template parts.
|
||||
- Diagnostics.
|
||||
- Compile and preflight limits.
|
||||
|
||||
Manual prose remains outside generated markers.
|
||||
|
||||
### Resume JSON Schema
|
||||
|
||||
The generator computes the canonical Resume JSON Schema once from `resumeDataSchema` using Zod's JSON Schema
|
||||
conversion.
|
||||
|
||||
That canonical schema drives two outputs:
|
||||
|
||||
1. `skills/resume-builder/references/schema.md`
|
||||
- A compact, AI-friendly Markdown reference.
|
||||
- Field hierarchy, types, required fields, constraints, and representative shapes.
|
||||
- Derived from the canonical JSON Schema rather than maintained separately.
|
||||
|
||||
2. `docs/guides/json-resume-schema.mdx`
|
||||
- The complete canonical JSON Schema inside a generated, marker-delimited JSON block.
|
||||
- Human-written explanation remains outside the generated block.
|
||||
|
||||
### OpenAPI specification
|
||||
|
||||
OpenAPI generation is exposed through one reusable, pure generator owned by `apps/server/src/openapi`.
|
||||
|
||||
- The runtime `/api/openapi/spec.json` handler calls it with `env.APP_URL`.
|
||||
- A sibling server documentation-generation script calls it with `https://rxresu.me` and writes `docs/spec.json`.
|
||||
- The root `docs:gen` command orchestrates the tooling generator and this server-owned OpenAPI generator.
|
||||
- The checked-in output is `docs/spec.json`.
|
||||
- The API version comes from the current application version.
|
||||
|
||||
This removes drift between runtime OpenAPI output and the checked-in documentation artifact, including stale versions
|
||||
and localhost server URLs.
|
||||
|
||||
### Determinism and failure behavior
|
||||
|
||||
Generation must:
|
||||
|
||||
- Produce stable ordering and formatting.
|
||||
- Require every expected marker.
|
||||
- Fail on duplicate or missing markers.
|
||||
- Fail on inconsistent template-manifest coverage.
|
||||
- Avoid silently leaving a partially updated reference that appears authoritative.
|
||||
|
||||
The generator computes all output text before writing any target. It does not add a general transaction framework.
|
||||
|
||||
## Custom Styles help hint
|
||||
|
||||
The Semantic CSS editor's shared chrome displays this hint directly above the code editor:
|
||||
|
||||
> **Not sure what to write?** Browse the Semantic CSS language reference.
|
||||
|
||||
The link:
|
||||
|
||||
- Targets `https://docs.rxresu.me/guides/semantic-css-reference`.
|
||||
- Opens in a new tab.
|
||||
- Uses `rel="noopener noreferrer"`.
|
||||
- Uses the existing `BookOpenIcon`, marked as decorative.
|
||||
- Has translated visible text.
|
||||
- Includes translated screen-reader text indicating that it opens in a new tab.
|
||||
- Appears in both the standard desktop editor and the mobile focus sheet because both use the same editor chrome.
|
||||
|
||||
The implementation stays local to the stylesheet editor. It does not introduce a shared component or central URL
|
||||
registry for one link.
|
||||
|
||||
## Documentation navigation
|
||||
|
||||
`docs/docs.json` lists `guides/semantic-css-reference` immediately after `guides/using-custom-styles`.
|
||||
|
||||
The public route is:
|
||||
|
||||
`https://docs.rxresu.me/guides/semantic-css-reference`
|
||||
|
||||
## Verification
|
||||
|
||||
### Generator verification
|
||||
|
||||
- `pnpm docs:gen` regenerates all four artifact groups.
|
||||
- A non-mutating test generates into temporary files and compares them byte-for-byte with committed outputs.
|
||||
- Generated output is deterministic across repeated runs.
|
||||
- Every runtime template part appears in the generated template matrix.
|
||||
- Cross-registry checks reject inconsistent template-part parent or child coverage.
|
||||
- The generated OpenAPI document matches the shared runtime generator for the documentation URL and current version.
|
||||
- Both schema Markdown targets are derived from the same canonical Resume JSON Schema.
|
||||
|
||||
### Example verification
|
||||
|
||||
- Complete copy-paste examples marked as valid compile successfully.
|
||||
- Selected intentionally invalid examples produce their documented diagnostic.
|
||||
- Small illustrative fragments that are not complete stylesheets are not forced through a full compiler test.
|
||||
|
||||
### UI verification
|
||||
|
||||
The stylesheet editor test verifies:
|
||||
|
||||
- Accessible link name.
|
||||
- Exact public URL.
|
||||
- New-tab target.
|
||||
- `noopener noreferrer`.
|
||||
- Presence in the standard editor.
|
||||
- Presence in the mobile focus sheet.
|
||||
|
||||
### Focused gates
|
||||
|
||||
- Tooling tests and typecheck.
|
||||
- Resume/schema tests and typechecks affected by exported metadata.
|
||||
- PDF manifest/reference consistency tests and typecheck.
|
||||
- API/server OpenAPI tests and typechecks.
|
||||
- Web editor tests and typecheck.
|
||||
- Workspace boundary check.
|
||||
- Focused formatting and Markdown validation.
|
||||
|
||||
Chrome verification is not required.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Contributor/compiler architecture documentation.
|
||||
- A second Semantic CSS reference route.
|
||||
- Splitting the reference across multiple pages.
|
||||
- Interactive documentation playgrounds.
|
||||
- New editor completion or hover features.
|
||||
- New Semantic CSS syntax or rendering behavior, except for correcting factual registry inconsistencies required to generate an
|
||||
accurate reference.
|
||||
- General documentation URL centralization.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- The canonical reference documents every author-facing Semantic CSS selector, semantic element, property, variable, directive,
|
||||
value family, template part, diagnostic family, limit, and unsupported syntax category.
|
||||
- The reference contains copy-paste examples for common author goals.
|
||||
- Generated facts come from authoritative runtime metadata and have staleness coverage.
|
||||
- `pnpm docs:gen` refreshes the Semantic CSS tables, both Resume JSON Schema references, and `docs/spec.json`.
|
||||
- Runtime and checked-in OpenAPI output share one generator.
|
||||
- The reference is visible in documentation navigation.
|
||||
- The Custom Styles editor links to the exact public reference route on desktop and mobile.
|
||||
- No unrelated product behavior or documentation architecture is introduced.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Semantic CSS Complete Rename Design
|
||||
|
||||
## Goal
|
||||
|
||||
Use **Semantic CSS** as the feature's only name. Remove the former acronym and its prefixes before the feature is merged
|
||||
so authors, contributors, diagnostics, and documentation all use one vocabulary.
|
||||
|
||||
## Naming Contract
|
||||
|
||||
The rename applies to every tracked source, test, fixture, generated marker, guide, plan, and specification in this
|
||||
branch. Git history is not rewritten.
|
||||
|
||||
| Context | Canonical form |
|
||||
| --- | --- |
|
||||
| Product and language name | Semantic CSS |
|
||||
| TypeScript symbol form | `SemanticCss*` |
|
||||
| Constant prefix | `SEMANTIC_CSS_*` |
|
||||
| Slug and cache form | `semantic-css-*` |
|
||||
| Version directive | `@version 1;` |
|
||||
| System variables | `--resume-*` |
|
||||
| Renderer properties | `-resume-*` |
|
||||
| Empty source constant | `EMPTY_SEMANTIC_CSS_SOURCE` |
|
||||
| Documentation markers | `SEMANTIC-CSS-*` |
|
||||
|
||||
Existing neutral names remain unchanged, including `stylesheet`, `mode: "semantic"`, `languageVersion`, semantic node
|
||||
names, API routes, database columns, and the `/applying-custom-styles` documentation URL.
|
||||
|
||||
## Language Syntax
|
||||
|
||||
New stylesheets and formatted output start with:
|
||||
|
||||
```css
|
||||
@version 1;
|
||||
```
|
||||
|
||||
Resolved builder values use the `--resume-*` namespace:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--accent: var(--resume-primary-color);
|
||||
}
|
||||
```
|
||||
|
||||
Renderer-specific properties use the `-resume-*` namespace:
|
||||
|
||||
```css
|
||||
section {
|
||||
-resume-min-presence-ahead: 24pt;
|
||||
}
|
||||
```
|
||||
|
||||
The compiler accepts only the new syntax. There are no deprecated aliases, conversion paths, or compatibility warnings
|
||||
because the feature has not shipped.
|
||||
|
||||
## Product and Documentation
|
||||
|
||||
All visible editor labels, help text, errors, diagnostics, logs intended for operators, tests that assert visible copy,
|
||||
and the Applying Custom Styles guide say **Semantic CSS**. The guide and examples teach only `@version`,
|
||||
`--resume-*`, and `-resume-*`.
|
||||
|
||||
The guide remains manually authored. `pnpm docs:gen` continues to regenerate only the Resume schema references and
|
||||
OpenAPI specification; example compilation tests continue to validate the guide's marked Semantic CSS examples.
|
||||
|
||||
## Internal Code
|
||||
|
||||
Public package exports and internal identifiers use `SemanticCss` or `SEMANTIC_CSS` when the language name is required.
|
||||
Identifiers already scoped by a stylesheet module may retain a neutral `Stylesheet*` name instead of repeating
|
||||
`SemanticCss`.
|
||||
|
||||
The compiler build/cache identifier changes so cached output produced under the old grammar cannot be reused. No data
|
||||
migration is added.
|
||||
|
||||
## Verification
|
||||
|
||||
The implementation is complete when:
|
||||
|
||||
1. A case-insensitive tracked-file search finds no occurrence of the former four-letter acronym.
|
||||
2. A tracked-file search finds none of the former directive, variable, or renderer-property prefixes.
|
||||
3. Compiler tests prove `@version 1;` is required and the old directive is rejected as unsupported.
|
||||
4. Registry and rendering tests cover the renamed system variables and renderer properties.
|
||||
5. The public guide's marked examples compile.
|
||||
6. Focused package tests, typechecks, documentation generation, Knip, Biome, and the existing E2E workflow pass.
|
||||
7. No local Chrome run is required; browser verification remains CI-owned.
|
||||
Reference in New Issue
Block a user