Files
Reactive-Resume/docs/applying-custom-styles.mdx
T
Amruth Pillai 99ff19e4e8 feat(templates): add Porygon and Smeargle templates
Porygon lays the resume out as a ruled form after the Japanese résumé
form (rirekisho). The header is a block of bordered field cells framed
in the accent color with the photo in a cell of its own, and every
section is a table with a solid title bar over one ruled cell per
entry. Touching cells share one tinted rule.

Smeargle sets the resume like a magazine feature: the headline as an
uppercase kicker over a display name, the summary as an italic
standfirst, and italic section headings at the user's heading size.

Both are one-column and ATS-safe. Register them in the schema, renderer,
semantic manifests and gallery, add gallery and docs previews, extract
the new descriptions, regenerate the API spec, and list them wherever
the docs enumerate templates.
2026-10-02 01:18:59 +02:00

429 lines
26 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "Applying custom styles"
description: "Write CSS rules that change how your resume PDF looks: pick an element on the page, style it, and learn what the PDF engine supports."
---
Custom styles let you change details the Design controls don't cover, such as the color of one entry, the spacing
between skills, or a border under every section heading. You write them in a CSS-like language called Semantic CSS,
and they apply to the PDF: the page preview, PDF downloads, and your public resume. DOCX and Markdown exports
don't use them.
Try the regular **Design** controls first (template, type, color, page and layout). They cover most changes and keep
working when you switch templates. Reach for custom styles when you want something specific.
## Before you start
- Custom styles are per resume. Each resume has its own stylesheet.
- The editor reports fatal stylesheet limits. Unsupported declarations or selectors may still be skipped; the
engine limitations are listed beside the editor. If you see no change on the page, check those limits. See [When a rule has no effect](#when-a-rule-has-no-effect).
- Custom styles can't add content, load fonts or images, or change what a section contains. They only restyle what's
already on the page.
## Open the stylesheet editor
<Steps>
<Step title="Switch to Design">
Open your resume and select **Design** at the top of the editor, or press <kbd>2</kbd> when you're not typing in a
field.
</Step>
<Step title="Open Advanced">
Select **Advanced** in the row of groups at the top of the Design panel. The Advanced group opens and scrolls into
view.
</Step>
<Step title="Find Custom Styles">
Scroll to **Custom Styles**, near the end of the Advanced group, just above **Reset design defaults**.
</Step>
</Steps>
<Frame caption="The Custom Styles editor, at the end of Design → Advanced">
<img
src="/images/guides/applying-custom-styles/custom-styles-editor.webp"
alt="The Custom Styles heading with a toolbar of five icons (undo, redo, copy, format, focus mode), two hint lines, and an empty code editor"
/>
</Frame>
On a phone, Design opens as a sheet over the page. Select the **Page** tab, then select **Advanced** under the page
settings.
<Note>
**Reset design defaults** doesn't touch your custom styles. It resets the look (fonts, colors, spacing) and keeps your
stylesheet, paper size, language, date format and section layout.
</Note>
## Style one section or entry
The quickest way to start is to pick the thing you want to change on the page.
<Steps>
<Step title="Click it on the page">
With the Custom Styles editor open, click the resume header, an entry (for example a job under Experience), or a
section without entries (such as Summary) on the page.
</Step>
<Step title="Type your declarations">
The editor adds a rule for it at the end of your stylesheet, with a comment naming what you picked, and puts the
cursor inside the rule. Type the declarations you want, for example `color: #0f766e;`.
</Step>
<Step title="Check the page">
The page updates as you type. While your cursor is inside a rule, everything the rule styles is outlined with a
dashed line on the page.
</Step>
</Steps>
<Frame caption="Clicking the University of Washington entry added a rule for it; the dashed outline shows what the rule styles">
<img
src="/images/guides/applying-custom-styles/pick-an-entry-on-the-page.webp"
alt="The stylesheet editor with a rule for the Education entry University of Washington, and the same entry on the page shown in teal inside a dashed outline"
/>
</Frame>
A picked rule looks like this:
```css
/* Education › University of Washington */
section[id="education"] item[id="019bef5a-93e4-7746-ad39-48455f6cef9e"] {
color: #0f766e;
}
```
If the stylesheet already has a rule with that exact selector, clicking the element moves your cursor into the existing
rule instead of adding another one.
Rules made this way use the entry's ID, so they only affect that one entry in this resume. To style every entry of a
kind, use the section type instead (see [Target the right part of your resume](#target-the-right-part-of-your-resume)).
## Find an entry by name
You can also add an entry's selector without clicking the page. Where a selector goes (outside any `{ }`), start
typing part of the entry's title, such as `Senior`. The suggestions include matching sections and entries, shown as
"Section › Entry". Choose one to insert its selector.
<Frame caption="Typing “Senior” suggests the Senior Game Developer entry; choosing it inserts its selector">
<img
src="/images/guides/applying-custom-styles/find-an-entry-by-name.webp"
alt="Autocomplete suggestions under the word Senior, led by Experience › Senior Game Developer with its section and item selector"
/>
</Frame>
Autocomplete also suggests element names and attributes in selectors, property names inside a rule, and values after
a colon, including your own variables and the read-only `--resume-*` variables. Press <kbd>Ctrl</kbd>
<kbd>Space</kbd> to open the suggestions without typing.
A few more editor helpers:
- **Hover** an element name, property or `--resume-*` variable to see a short description.
- **Color swatches** appear after color values. Click one to pick a color from presets or a custom picker; the value
in your CSS changes with it.
- **Search** with <kbd>⌘</kbd> <kbd>F</kbd> (<kbd>Ctrl</kbd> <kbd>F</kbd> on Windows and Linux).
## Use the editor toolbar
| Button | What it does |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| **Undo resume change** / **Redo resume change** | Steps back or forward through your resume's changes. |
| **Copy stylesheet** | Copies the whole stylesheet to your clipboard. |
| **Format stylesheet** | Tidies indentation and spacing. |
| **Open focus mode** | Opens the editor in a larger side panel titled **Semantic CSS stylesheet**. Select **Exit focus mode** or close the panel to go back. |
While typing in the stylesheet editor, <kbd>⌘</kbd> <kbd>Z</kbd> and <kbd>⌘</kbd> <kbd>Shift</kbd>
<kbd>Z</kbd> use the editor's local text history. The toolbar buttons use your resume's shared history, so they can also
reverse a template switch or another resume change. See [Undoing changes and version
history](/guides/undoing-changes-and-version-history).
Your stylesheet saves automatically with the rest of the resume, and it's part of every saved version in **History**.
## When a rule has no effect
Fatal stylesheet errors appear above the editor. For individual rules, the PDF engine applies what it understands and skips the rest: an
unsupported declaration is skipped on its own, and a rule with an invalid selector is skipped as a whole. If a change
doesn't show:
1. **Check the selector matches something.** Put your cursor inside the rule. If nothing is outlined on the page, the
selector matches nothing. Check spelling (names are lowercase and case-sensitive), attribute values, and whether
the current template has that part.
2. **Check the property is supported** for that element. See the [properties](#properties) and
[engine limits](#engine-limits) below. For example, `background-color` works on containers such as `section` and
`header`, but not on text elements such as `field`.
3. **Simplify.** Reduce the rule to one selector and one declaration, confirm it works, then add more.
4. **Check the limit message.** More than 128 KB, 1,024 rules, 8,192 declarations, 64 selectors in one rule or four nested media blocks makes the whole stylesheet unusable; the editor reports this.
<Warning>
Check the downloaded PDF before you send or share a resume with custom styles. Page breaks and template-specific
details can make a rule look different from what you intended.
</Warning>
### Engine limits
The PDF engine accepts these without an error but doesn't draw them:
- Rotation (`transform: rotate(...)`). The picture's Rotation control is disabled while the PDF engine lacks support.
- Dashed and dotted borders. They draw as solid lines.
- Percentages for padding, margin, gaps and font sizes. Percentages work only for `width`, `height`, their `min-` and
`max-` forms, `flex-basis`, `left` and `right`.
- `z-index`, `max-lines`, `text-indent`, `vertical-align`, `object-position` and `-resume-min-presence-ahead`.
Two text limits apply whether or not you use custom styles: right-to-left lines are laid out left to right and then
aligned right, and characters outside the basic range, such as most emoji, don't draw.
## Copy styles to another resume
Section types, element names and field names work in any resume. Rules that use an entry's `id` only match that
entry, and template parts only exist in their template.
1. In the source resume, select **Copy stylesheet**.
2. In the other resume, open **Design → Advanced → Custom Styles** and paste.
3. Remove or rewrite rules with `item[id=...]` selectors, and template-part rules for a different template.
4. Check the page and the downloaded PDF.
## Reference
### Target the right part of your resume
Semantic CSS selectors describe resume content, not a template's internal layout. Element names and attribute names
are lowercase and case-sensitive.
**Resume structure**
| Element | Targets | Typical use |
| ----------------- | ---------------------------------------------------- | ------------------------------------------------------- |
| `resume` | The whole resume | Scope a rule to one template. |
| `page` | One PDF page | Set a page size. |
| `region` | The header, main, sidebar or featured area of a page | Style a layout area. |
| `header` | The resume header | Style the name and contact area. |
| `section` | A section | Target a section type or placement. |
| `section-heading` | A section's title | Change heading type or add a rule under it. |
| `section-items` | The entries in a section | Adjust entry layout and gaps. |
| `item` | One entry | Space or keep together a job, project or similar entry. |
| `item-header` | An entry's top row | Align the title, company, dates or similar details. |
**Header and entry content**
| Element | Targets |
| ------------------------------ | --------------------------------------------------- |
| `picture` | The profile picture. |
| `name`, `headline` | The name and headline in the header. |
| `contact-list`, `contact-item` | The contact details in the header. |
| `combined-text` | A value the template builds from several fields. |
| `field` | A named field, such as a position, company or date. |
| `link` | A link. |
| `icon`, `level` | An icon, or a skill or language level indicator. |
**Rich text and lists**
| Element | Targets |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| `rich-text`, `rich-heading`, `blockquote`, `paragraph` | Formatted text blocks in descriptions and the summary. |
| `list`, `list-item`, `list-marker`, `list-item-content` | A list, one list row, its bullet or number, and its text. |
| `strong`, `emphasis`, `underline`, `strike`, `code`, `text-span`, `mark` | Inline formatting. |
| `hard-break`, `horizontal-rule` | A line break or horizontal rule. |
| `template-part` | A template-specific detail. Always guard it with a template (see below). |
### Narrow a selector with attributes
| Attribute | Use it with | Example |
| ------------- | --------------------------------------------------------- | ------------------------------------ |
| `type` | `section`, `icon` | `section[type="experience"]` |
| `placement` | `region`, `section` | `region[placement="sidebar"]` |
| `region` | `region`, `header` | `region[region="featured"]` |
| `origin` | `section` | `section[origin="main"]` |
| `part` | `region`, `section`, `contact-item`, `item-header` | `region[part~="sidebar-background"]` |
| `template` | `resume` | `resume[template="azurill"]` |
| `name` | `field`, `contact-item`, `combined-text`, `template-part` | `field[name="position"]` |
| `level` | `rich-heading` | `rich-heading[level="2"]` |
| `direction` | `list-item-content` | `list-item-content[direction="rtl"]` |
| `page-number` | `page` | `page[page-number="1"]` |
| `id` | Any element that has one | `section[id="projects"]` |
| `role` | Any element with roles | `field[role~="secondary-text"]` |
Attribute matchers `=`, `~=`, `^=` and `*=` are supported. `#projects` is shorthand for `[id="projects"]` when the ID
is a plain word (section IDs such as `experience` are; entry IDs aren't, so use `item[id="..."]`).
Selectors can be combined with commas, and with the descendant (space), child (`>`), next-sibling (`+`) and
later-sibling (`~`) combinators. Supported pseudo-classes: `:root`, `:first-child`, `:last-child`, `:only-child`,
`:nth-child()`, `:nth-of-type()`, `:is()`, `:where()` and `:not()`. `:nth-of-type` counts elements of the same kind,
so `item:nth-of-type(1)` is the first entry.
```css
section[type="experience"] > section-heading {
border-bottom: 1pt solid #0f766e;
}
region[placement="sidebar"] {
background-color: #f8fafc;
padding: 18pt;
}
section[type="experience"] item:nth-of-type(1) {
margin-bottom: 12pt;
}
```
### Reuse your Design settings
Your Design settings are available as read-only `--resume-*` variables. Define your own variables in `:root` and
reuse the resume's values, so your styles follow when you change a color or size in Design.
```css
:root {
--accent: var(--resume-primary-color);
--rule: #cbd5e1;
}
section-heading {
color: var(--accent);
border-bottom: 1pt solid var(--rule);
letter-spacing: 0.4pt;
}
```
Don't assign a value to a `--resume-*` variable; create your own, such as `--accent`, instead.
| Design setting | Read-only variables |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Colors | `--resume-primary-color`, `--resume-text-color`, `--resume-background-color` |
| Type | `--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 sizes in a PDF. Lengths also accept `px`, `in`, `mm`, `cm`, `em` and `rem`, and `%` where the
[engine limits](#engine-limits) allow it. `inherit` and `initial` work as values, and `!important` is respected.
### Properties
| Goal | Properties |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Text | `color`, `font-size`, `font-style`, `font-weight`, `letter-spacing`, `line-height`, `text-align`, `text-decoration`, `text-transform`, `text-overflow`, `direction` |
| Spacing and size | `margin`, `padding` (and their side forms), `gap`, `row-gap`, `column-gap`, `width`, `height`, `min-width`, `max-width`, `min-height`, `max-height`, `aspect-ratio` |
| Layout | `display` (`flex` or `none`), `flex`, `flex-direction`, `flex-wrap`, `justify-content`, `align-items`, `align-self`, `order`, `position`, `top`, `right`, `bottom`, `left`, `overflow` |
| Look | `background-color`, `border` (and its side, width, style, color and radius forms), `opacity`, `transform`, `transform-origin` |
| Picture | `object-fit`, `-resume-shadow-color`, `-resume-shadow-width` |
| Page breaks | `break-before`, `break-inside`, `orphans`, `widows`, `size` (on `page` only) |
`background-color` and borders apply to container elements (such as `header`, `section`, `item`, `region`), not to
text elements. `display: none` hides an element that's already there; to remove content, hide it in **Write** instead,
so it's also left out of exports.
Backgrounds must be flat colors. Gradients such as `linear-gradient(...)` aren't supported:
```css
header {
background-color: #1e293b;
}
```
### Style common details
List rows, markers and text:
```css
rich-text list-item {
gap: 4pt;
}
list-marker {
color: var(--resume-primary-color);
}
list-item-content {
line-height: 1.35;
}
```
Space between a skill's level icons (`gap` works too; `row-gap` doesn't change this single row):
```css
section[type="skills"] level {
column-gap: 4pt;
}
```
Named fields inside entries:
```css
section[type="experience"] field[name="position"] {
font-weight: 600;
}
section[type="experience"] field[name="company"] {
color: var(--resume-primary-color);
}
```
### Control page breaks and page size
Start a section on a new page, keep entries whole, or set a custom page size. Review the downloaded PDF after each
change.
```css
page {
size: 210mm 297mm;
}
section[type="projects"] {
break-before: page;
}
item {
break-inside: avoid;
}
```
`size` takes `A4`, `letter`, or a width and height. It applies only to `page` and can't be inside `@media`.
`@media` queries use the PDF page's dimensions, not your browser window. Supported features are `width`, `min-width`,
`max-width`, `height`, `min-height`, `max-height` and `orientation` (`portrait` or `landscape`).
```css
@media (max-width: 600pt) {
region[placement="sidebar"] {
padding: 12pt;
}
}
```
### Template-specific parts
Some templates expose extra details that others don't have. Always guard these rules with `resume[template="..."]`, so
they don't apply by accident after a template change.
```css
resume[template="azurill"] template-part[name="timeline-line"] {
background-color: #94a3b8;
}
```
| Template | 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 | `template-part[name="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"]` |
| Smeargle | `template-part[name="featured-summary"]` |
Kakuna, Lapras, Onyx and Porygon have no template-specific parts.
Every template also has `template-part[name="item-header-row"]`: the row with the title and date in Awards,
Certifications, Projects and Publications entries. It doesn't need a template guard.
### Not supported
Classes, pseudo-elements (`::before`, `::after`), CSS Grid, `@import`, `@font-face` and other at-rules except
`@media`, `url()` and any external file, gradients, general box shadows, filters, animations and transitions. Use the
Design controls for fonts, the picture and broader layout changes.
## Related guides
- [Choosing a template](/guides/choosing-a-template): a different template may get you most of the way without CSS.
- [Customizing typography](/guides/customizing-typography): fonts, sizes and line height from Design.
- [Arranging the layout](/guides/arranging-the-layout): columns, sidebar and page breaks without CSS.
- [Undoing changes and version history](/guides/undoing-changes-and-version-history): get back to a stylesheet that worked.