Overview

Customization: Style, Theming & Localization

You can customize PWAxcode in three main ways:

  • CSS styling (tokens, lines, fold summaries, search highlights, fades, UI buttons).
  • Themes (switch built-ins or register a custom theme id and style it).
  • Localization (i18n: add/extend locales and switch language at runtime).

CSS hooks


  • Root state
    • .PWAxcode
    • Varaints: .pxc-fullscreen, .pxc-compressed, .pxc-no-linenumbers, .pxc-toolbar-hidden
  • Lines
    • .pxc-line[data-ln] (each line; data-ln holds the line number)
    • .pxc-line.hit (line containing a search hit)
    • .pxc-line.curr (current line, if used)
    • .pxc-line.marked (marked via API)
  • Tokens (highlighted view)
    • [data-tok="kw|fn|str|num|com|lit|key|tag|attr|ent|doctype|prop|sel|pun"]
  • Search highlights
    • .pxc-hitpart (inline fragment highlight inside a line)
  • Folding
    • .pxc-fold-start (line that starts a foldable region)
    • .pxc-folded (content lines folded away)
    • .pxc-fold-summary (the summary row like “… n lines collapsed”)
  • Fades (overflow hint)
    • .pxc-fade-top, .pxc-fade-bottom (+ .show when active)
  • UI controls
    • .pxc-copy, .pxc-toggle, .pxc-download, .pxc-fullscreen, .pxc-collapse
    • Footer / float bar: .pxc-footbar, .pxc-fbtn, .pxc-floatbar
  • Range highlights (overlay boxes added via API)
    • default class is pxc-rangehl (and any custom class you pass)
Typical token overrides (example)
These selectors are stable and easy to theme per-instance:
/* Example token palette */ .PWAxcode [data-tok="kw"] { color:#93c5fd; } .PWAxcode [data-tok="fn"] { color:#a78bfa; } .PWAxcode [data-tok="str"] { color:#86efac; } .PWAxcode [data-tok="num"] { color:#fca5a5; } .PWAxcode [data-tok="com"] { color:#94a3b8; font-style:italic; opacity:.9; }

Styling lines, current row, marks, and search:
/* Current line */ .PWAxcode .pxc-line.curr { background: rgba(125, 211, 252, .08); } /* Marked lines (via api.mark) */ .PWAxcode .pxc-line.marked { background: rgba(250, 204, 21, .12); } /* Lines with a search hit */ .PWAxcode .pxc-line.hit { outline: 1px dashed rgba(147, 197, 253, .6); } /* Inline hit fragments */ .PWAxcode .pxc-hitpart { background: rgba(59, 130, 246, .18); border-radius: 3px; }

Folding summary row:
/* Fold header line and summary row */ .PWAxcode .pxc-fold-start { position: relative; } .PWAxcode .pxc-fold-summary { background: rgba(148, 163, 184, .12); border-left: 3px solid #94a3b8; padding: 2px 8px; cursor: pointer; } .PWAxcode .pxc-fold-summary:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; } .PWAxcode .pxc-fold-summary:hover { background: rgba(148, 163, 184, .18); }

Fade gradients (when content overflows):
/* Top/bottom fade hints */ .PWAxcode .pxc-fade-top, .PWAxcode .pxc-fade-bottom { opacity: 0; transition: opacity .2s linear; pointer-events: none; } .PWAxcode .pxc-fade-top.show { opacity: 1; background: linear-gradient(#0b1020, rgba(11,16,32,0)); } .PWAxcode .pxc-fade-bottom.show { opacity: 1; background: linear-gradient(rgba(11,16,32,0), #0b1020); } /* Light theme variant */ .PWAxcode[data-theme="light"] .pxc-fade-top.show { background: linear-gradient(#fff, rgba(255,255,255,0)); } .PWAxcode[data-theme="light"] .pxc-fade-bottom.show { background: linear-gradient(rgba(255,255,255,0), #fff); }

Footer & floating controls:
/* Footer bar container */ .PWAxcode .pxc-footbar { gap: .5rem; } /* Buttons in foot/float bars */ .PWAxcode .pxc-fbtn { border-radius: 10px; padding: 6px 10px; line-height: 1; } .PWAxcode .pxc-fbtn:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; } /* Floatbar chrome */ .PWAxcode .pxc-floatbar { box-shadow: 0 8px 24px rgba(0,0,0,.2); border-radius: 12px; backdrop-filter: blur(4px); }

Range highlights (added with api.addRangeHighlight):
/* Default overlay boxes */ .PWAxcode .pxc-rangehl { background: rgba(16, 185, 129, .15); border: 1px solid rgba(16, 185, 129, .35); border-radius: 6px; } /* Alternate style you can pass: className:'pxc-rangehl green' */ .PWAxcode .pxc-rangehl.green { background: rgba(99, 102, 241, .14); border-color: rgba(99, 102, 241, .35); }

Theming basics


  • Built-in themes: light, dark, funky, relax, plus auto (follows system).
  • Custom themes: choose an id (e.g., "mybrand"), then either:
    • set it via options and override with CSS using attribute/selector scoping, or
    • define your own CSS custom properties if your build uses them.
Switch at runtime:
api.setOption('theme', 'dark'); // or 'light' | 'funky' | 'relax' | 'auto' | 'mybrand' // or api.setTheme('light');

Example: theme-scoped overrides without relying on variables:
/* Only when the component’s theme is "mybrand" */ .PWAxcode[data-theme="mybrand"] { background: #0b0e14; color: #e6e6e6; } .PWAxcode[data-theme="mybrand"] [data-tok="kw"] { color:#7aa2f7; } .PWAxcode[data-theme="mybrand"] [data-tok="fn"] { color:#bb9af7; } .PWAxcode[data-theme="mybrand"] [data-tok="str"] { color:#9ece6a; } .PWAxcode[data-theme="mybrand"] [data-tok="num"] { color:#f7768e; } .PWAxcode[data-theme="mybrand"] [data-tok="com"] { color:#6b7280; font-style:italic; }
Tip: If your build defines CSS variables for themes, set them under [data-theme="mybrand"] and keep selectors minimal. Otherwise, override token colors directly as above.
State variants worth theming
/* Fullscreen & compressed modes */ .PWAxcode.pxc-fullscreen { z-index: 9999; background: #0b0e14; } .PWAxcode.pxc-compressed .pxc-footbar { display:none; } .PWAxcode.pxc-toolbar-hidden .pxc-toggle { display:none; } /* When line numbers are hidden */ .PWAxcode.pxc-no-linenumbers .pxc-line[data-ln] { padding-left: .5rem; }

Accessibility & motion
/* Keep focus visible and high-contrast */ .PWAxcode :focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; } /* Respect reduced motion */ @media (prefers-reduced-motion: reduce) { .PWAxcode * { transition: none !important; animation: none !important; } }

Custom themes: what the API actually supports

  • Register a theme id (global):
    Use the static method PWAxcode.registerTheme(name, opts?). It only registers the id (and optional alias), it does not accept a color palette.
    // Make "ocean" a valid theme id PWAxcode.registerTheme('ocean'); // Optional: register an alias that resolves to an existing theme // (useful if you want "midnight" to behave like "dark" until you add CSS) PWAxcode.registerTheme('midnight', { aliasOf: 'dark' });

  • Switch the active theme (instance):
    Call api.setTheme('ocean') (or api.setOption('theme', 'ocean')). api.getTheme() returns the effective theme; api.listThemes() (or PWAxcode.listThemes()) shows all registered ids.
    api.setTheme('ocean'); // switch now api.setOption('theme', 'relax'); // equivalent console.log(api.getTheme()); // 'ocean' | 'dark' | 'light' | ... console.log(api.listThemes()); // ['dark','light','funky','relax','ocean', ...]

  • Automatic theme:
    Passing auto (or system) makes the component follow the OS preference (Light/Dark) via matchMedia.
    Important: There is no instance method registerTheme(...). Use PWAxcode.registerTheme(...) (static). Also, setTheme({...}) with a palette object is not supported and will be rejected.

How to style a custom theme

Registering a theme id only tells the component that the id is valid. The colors come from your CSS (scoped by the data-theme attribute on the root).
Create rules for your theme id:

/* Ocean theme base */ .PWAxcode[data-theme="ocean"] { background: #0b132b; color: #e0e6f0; } /* Line numbers (typical implementation uses ::before on [data-ln]) */ .PWAxcode[data-theme="ocean"] .pxc-line[data-ln]::before { color: #94a3b8; opacity: .85; } /* Token colors */ .PWAxcode[data-theme="ocean"] [data-tok="kw"] { color:#4cc9f0; } .PWAxcode[data-theme="ocean"] [data-tok="fn"] { color:#80ed99; } .PWAxcode[data-theme="ocean"] [data-tok="str"] { color:#bde0fe; } .PWAxcode[data-theme="ocean"] [data-tok="num"] { color:#ffd166; } .PWAxcode[data-theme="ocean"] [data-tok="com"] { color:#8e9aaf; font-style: italic; opacity:.9; } /* Optional: gutter/accents if your stylesheet exposes hooks */ .PWAxcode[data-theme="ocean"] .pxc-fold-summary { background: rgba(255,255,255,.06); border-left-color:#94a3b8; }

Then:
PWAxcode.registerTheme('ocean'); // make the id official api.setTheme('ocean'); // activate it

Correct approach:


  • Use PWAxcode.registerTheme('ocean') (static) to make the id valid.
  • Put your palette in CSS under .PWAxcode[data-theme="ocean"] ….
  • Activate with api.setTheme('ocean') or api.setOption('theme','ocean').
If you want “ocean” to temporarily behave like an existing theme while you work on CSS, you can alias it:
PWAxcode.registerTheme('ocean', { aliasOf: 'dark' }); api.setTheme('ocean'); // accepted thanks to the alias // (still add your CSS for [data-theme="ocean"] when ready)

Localization (i18n)


Initialize with translations (merge into base dictionaries):
import { fr } from './fr.js'; import { es } from './es.js'; new PWAxcode(root, { locale: 'fr', // or 'es' translations: { fr, es } // merged with built-in base strings });

At runtime (for an existing instance):
import { fr } from './fr.js'; import { es } from './es.js'; api.extendLocale('fr', fr); api.extendLocale('es', es); api.setLocale('fr'); // switch UI to French

Regional variants and partial overrides:
api.extendLocale('fr-CA', { ui: { fullscreen: { enter: 'Plein écran', exit: 'Quitter le plein écran' } } }); api.setLocale('fr-CA');

Guidelines for dictionaries
  • Structure: keep the same nested shape as the base (sections like ui, msg, fold).
  • Placeholders: preserve tokens such as {n} (e.g., fold.summary.one, fold.summary.other).
  • Partial overrides are ok: missing keys fall back to the base strings.
  • Live updates: most UI strings refresh immediately; elements recreated later (e.g., toasts/popovers) will use the new locale automatically.
Example dictionaries:
export const fr = { ui: { copy: 'Copier le code', toggleHL: { help: 'Coloration syntaxique on/off', on: 'Coloration : ON', off: 'Coloration : OFF' }, download: 'Télécharger le code', fullscreen: { enter: 'Plein écran', exit: 'Quitter le plein écran' }, collapse: 'Réduire', expand: 'Développer', wrap: 'Retour à la ligne', wrapOn: 'Retour à la ligne : ON', wrapOff: 'Retour à la ligne : OFF', undo: 'Annuler', redo: 'Rétablir', popover: { question: 'Question', shure: 'Êtes-vous sûr ?', yes: 'Oui', no: 'Non', tap: 'Touchez pour continuer', continue: 'Continuer' }, toolbar: { help: 'Afficher/Masquer la barre d’outils', show: 'Afficher la barre d’outils', hide: 'Masquer la barre d’outils' }, floatbar: { help: 'Afficher/Masquer la barre mobile', show: 'Afficher la barre mobile', hide: 'Masquer la barre mobile' }, search: { prev: 'Précédent', next: 'Suivant' }, zoom: { in: 'Agrandir', out: 'Réduire', reset: 'Réinitialiser la police' }, player: { play: 'Lecture', pause: 'Pause', prev: 'Étape précédente', next: 'Étape suivante' }, }, msg: { copy: { ok: 'Copié !', errPerm: 'Erreur de copie (permissions)' }, search: { none: 'Aucun résultat' }, wait: 'Veuillez patienter…' }, fold: { summary: { one: '… {n} ligne repliée', other: '… {n} lignes repliées' } } }; export const es = { ui: { copy: 'Copiar código', toggleHL: { help: 'Resaltado de sintaxis on/off', on: 'Resaltado: ON', off: 'Resaltado: OFF' }, download: 'Descargar código', fullscreen: { enter: 'Pantalla completa', exit: 'Salir de pantalla completa' }, collapse: 'Contraer', expand: 'Expandir', wrap: 'Ajuste de línea', wrapOn: 'Ajuste de línea: ON', wrapOff: 'Ajuste de línea: OFF', undo: 'Deshacer', redo: 'Rehacer', popover: { question: 'Pregunta', shure: '¿Seguro?', yes: 'Sí', no: 'No', tap: 'Toca para continuar', continue: 'Continuar' }, toolbar: { help: 'Mostrar/Ocultar barra de herramientas', show: 'Mostrar la barra de herramientas', hide: 'Ocultar la barra de herramientas' }, floatbar: { help: 'Mostrar/Ocultar barra móvil', show: 'Mostrar barra móvil', hide: 'Ocultar barra móvil' }, search: { prev: 'Anterior', next: 'Siguiente' }, zoom: { in: 'Acercar', out: 'Alejar', reset: 'Restablecer fuente' }, player: { play: 'Reproducir', pause: 'Pausa', prev: 'Paso anterior', next: 'Paso siguiente' }, }, msg: { copy: { ok: '¡Copiado!', errPerm: 'Error al copiar (permisos)' }, search: { none: 'Sin resultados' }, wait: 'Por favor, espera…' }, fold: { summary: { one: '… {n} línea contraída', other: '… {n} líneas contraídas' } } };

Switching locale vs. theme at runtime
// Localization api.extendLocale('de', { ui: { copy: 'Code kopieren' } }); api.setLocale('de'); // instant UI switch where applicable // Theming api.setOption('theme', 'relax'); // or 'mybrand' if you provide CSS overrides

Practical checklist
  • Wrap all custom CSS under .PWAxcode (or under [data-theme="mybrand"]) for safety.
  • Override tokens with [data-tok=...] selectors; avoid brittle DOM assumptions.
  • Keep focus outlines visible; ensure 4.5:1 contrast.
  • Respect prefers-reduced-motion.
  • For i18n, only override what you need; keep placeholders like {n} intact.
  • Test changes in both highlighted and plain views, with/without line numbers, and in fullscreen/ compressed modes.