28 KiB
Notepad3 Schemas, Styles & Themes
This document describes how Notepad3's appearance customization works: what a schema (a.k.a. lexer style set) is, how styles are layered, how the style mini-language reads, how to export / import / switch themes, and where the user-editable files live.
Audience: advanced users who want to tweak colours and fonts beyond the built-in dialog, share a theme with others, or understand why a particular setting isn't sticking.
Table of contents
- Core concepts
- Layered override model
- The style mini-language
- The default schemas (
Common Base&2nd Common Base) - File → schema matching
- The Customize Schemes dialog
- Themes collection (
View → Themes) - Export / Import
- Anatomy of a style INI file
- Toolbar themes (separate from colour schemes)
- References in the code
1. Core concepts
| Term | Meaning |
|---|---|
| Schema / Scheme | A complete set of styles for one language (e.g. C/C++ Source Code, Python Script). Backed in code by an EDITLEXER record plus an array of EDITSTYLE entries. |
| Style | A single styleable element inside a schema (e.g. Default Style, Keywords, Comments, Strings). Each style has a plain-text style string describing its font, size, colours, etc. |
| Theme | A complete snapshot of all custom colours, schemas and per-schema extensions, stored as a standalone .ini file (e.g. Dark.ini). Switching themes replaces every schema's style strings at once. |
| Lexilla lexer | The underlying syntax-highlighting engine for a schema. Notepad3 ships with ~60 lexers (C++, Python, Markdown, Rust, YAML, …). |
| Custom colours | A 16-slot colour palette stored per-installation. Used by the Customize Schemes colour-picker and exported with every theme. |
Notepad3 ships with roughly 60 schemas; the exact list appears in the Select Scheme dialog (Ctrl+F11) and in the left-hand tree of Customize Schemes (F12).
2. Layered override model
Every style string you see in the editor is the result of a four-level resolution chain, applied each time a file is opened or a theme is switched:
┌─────────────────────────────────────────────────────────────┐
│ 4. Runtime edits (Customize Schemes dialog) │ ← highest priority, in-memory only
├─────────────────────────────────────────────────────────────┤
│ 3. User INI — either Notepad3.ini [SchemaSection] │
│ or the currently loaded theme file (themes/<Name>.ini) │
├─────────────────────────────────────────────────────────────┤
│ 2. 'Common Base' / '2nd Common Base' defaults │
│ (font, size, fg/bg for everything unstyled) │
├─────────────────────────────────────────────────────────────┤
│ 1. Hard-coded lexer defaults (compiled into Notepad3.exe) │ ← lowest priority
└─────────────────────────────────────────────────────────────┘
- Lower levels fill gaps only. If a user INI defines
fore:#ff0000for a style but omitsfont:, the font comes from the Common Base, and ultimately from the hard-coded default. - Only deltas are saved. When Notepad3 writes your styles back, it stores only values that differ from the merged defaults. An exported theme, by contrast, can force-save everything — see §8 Export/Import.
- Empty lexer sections are removed. After saving, any
[Python Script]-style section that ended up empty (because the user reset everything to default) is stripped so the file stays readable. - Dark mode is its own level 2. When Windows is in dark mode, the Common Base defaults come from a dark-mode palette instead of the light one. Your custom deltas stack on top of whichever palette is active.
Where do my custom changes land on disk?
| Active theme | Changes written to |
|---|---|
| Standard Config (index 0) | Notepad3.ini, sections [Styles], [Custom Colors], and per-schema sections like [C/C++ Source Code], [Python Script], … |
| A named theme (e.g. Dark) | <IniFileDir>\themes\Dark.ini (full dump — all styles, not just deltas) |
Saving occurs only if Settings.SaveSettings = 1 (Menu Settings → Save Settings On Exit) or you choose Settings → Save Settings Now (F7).
3. The style mini-language
Every EDITSTYLE::szValue is a semicolon-delimited list of attribute:value tokens. Order is irrelevant, keys are case-insensitive, and spacing around : and ; is tolerated:
font:Consolas; size:11; fore:#D7BA7D; back:#1E1E1E; bold
Buffer limit: 255 characters per style string (BUFSIZE_STYLE_VALUE in src/StyleLexers/EditLexer.h). Longer strings are silently truncated on load.
3.1 Attributes
| Token | Purpose | Examples |
|---|---|---|
font:<name> |
Font face. Literal face names or the aliases Default, $Code0…$Code9, $Text0…$Text9 which resolve to entries of CodeFontPrefPrioList / TextFontPrefPrioList (Settings2). |
font:Consolas, font:$Code0 |
size:<pt> or size:±<pt> |
Absolute (11) or relative (+2, -1) font size in points. Relative sizes cumulate across the inheritance chain. |
size:10, size:+1 |
fore:#RRGGBB |
Foreground (text) colour, hex. #AARRGGBB also accepted on styles that respect alpha. |
fore:#569CD6 |
back:#RRGGBB |
Background colour, hex. #AARRGGBB also accepted. |
back:#1E1E1E |
bold / italic / underline / strikeout |
Plain flags, no value. | italic |
eolfilled |
Extends the background colour to the end of the line. | eolfilled |
case:u | case:l | case:m |
Force upper / lower / mixed case (visual only). | case:u |
charset:<n> |
LOGFONT charset id (0 = ANSI, 128 = Shift-JIS, 186 = Baltic, 204 = Russian, …). Clamped to ≥ 0. | charset:204 |
smoothing:<q> |
Font quality. Tokens: default, standard, aliased, antialiased, cleartype. |
smoothing:cleartype |
alpha:<0-255> |
Primary alpha (indicator / overlay opacity). | alpha:40 |
alpha2:<0-255> |
Secondary alpha (outline for box-type indicators). | alpha2:220 |
weight:<name> |
Font weight. Accepted words: thin, extralight, light, book, regular, medium, semibold, bold, extrabold, heavy, extrablack, plus a handful of synonyms. |
weight:semibold |
stretch:<name> |
Font stretch: ultracondensed, extracondensed, condensed, semicondensed, normal, semiexpanded, expanded, extraexpanded, ultraexpanded. |
stretch:condensed |
block |
Draw caret as a block. | block |
hotspot |
Mark the style as a hotspot (clickable region). | hotspot |
indic_<shape> |
Indicator drawing style (used by hotspot / brace / occurrence markers). Common values: indic_plain, indic_squiggle, indic_tt, indic_diagonal, indic_strike, indic_box, indic_roundbox, indic_straightbox, indic_fullbox, indic_dash, indic_dots, indic_squigglelow, indic_squigglepixmap, indic_compositionthick, indic_compositionthin, indic_textfore, indic_point, indic_pointcharacter, indic_gradient, indic_gradientcentre. |
indic_roundbox |
3.2 Colour syntax
- Always
#followed by six hex digits (#RRGGBB) — not0xBBGGRRas in the dark-mode INI keys. - Named colours are not supported; use hex.
- Colours stored in the Custom Colors palette (the 16 slots at the top of any theme file) are written as
#RRGGBBstrings with keys01…16and are just a convenience shared with the Windows colour-picker dialog — they are not referenced from style strings.
3.3 Example — styling comments in C++
Comment=italic; fore:#608B4E; back:#1E1E1E
- Italic, no explicit font (inherits Common Base), explicit fg and bg.
- Key (
Comment) matches a fixed slot insidestyleLexCPP.c. You cannot invent new style names; you edit the ones the lexer defines.
4. The default schemas (Common Base & 2nd Common Base)
Two "super-schemas" act as the global fallback for every language-specific schema:
| Schema | INI section | Role |
|---|---|---|
Common Base |
[Common Base] |
Primary defaults — applied to every style that does not explicitly set an attribute. |
2nd Common Base |
[2nd Common Base] |
Alternate set, activated by Menu → View → Use 2nd Default Style (IDM_VIEW_USE2NDDEFAULT = 41002). |
Both schemas also contain the universal editor chrome styles (they are not language specific):
Default Style— base font, size, fg, bg.Margins and Line Numbers— line-number gutter.Matching Braces (Indicator)/Matching Braces Error (Indicator)— brace highlight.Control Characters— the[NUL],[BS]etc. glyphs.Indent Guide— vertical dotted lines.Selection— selection highlight (foreground/background, with alpha).Whitespace— dot/arrow/EOL glyphs.Current Line— background highlight of the line containing the caret.Caret— colour + width,blockmodifier.Long Line Marker— visual for the right-margin line.Bookmark/Foldingcolours.Mark Occurrences— the highlight colour of Mark All Occurrences.Hotspot Style/Unicode Hotspot/Multi-Edit— interactive overlays.Change Historymarkers (modified/saved/unsaved).IME Inputcolour.Invisible/Read-onlyoverlays.
Switching with Use 2nd Default Style
The status bar shows STD or 2ND in the INS/OVR field. Double-click there (or use the menu) to toggle. The active state is saved as:
[Styles]
Use2ndDefaultStyle=1
Both base schemas coexist in the INI: toggling only flips which one Notepad3 reads.
5. File → schema matching
When you open a file, Notepad3 runs a pipeline to pick the right schema. It stops at the first match.
- File variables (
vim:/emacs:modelines inside the file). If the file declaresmode: python;or-*- mode: cpp -*-, the declared mode name is matched against schema names (case-insensitive prefix) and then against extension lists. Disabled bySettings2.NoFileVariables=1. - Shebang detection for
.cgi/.fcgifiles or files flagged as CGI by mode. Recognised interpreters:python,ruby,bash/sh,perl,tcl,node/js,php. Disabled bySettings2.NoCGIGuess=1. - Wildcard / filename match. Any entry in a schema's extension list that contains
*,?, or.is matched against the bare filename via the standard Win32 wildcard matcher — e.g.Makefile*matchesMakefile,Makefile.dev,cmakelists.txtmatches the literal filename. See the Extension lists subsection below for syntax and worked examples. - Plain extension match. First lexer whose extension list contains the file's extension wins. Extension comparison is case-insensitive; separator is
;(semicolon or space both work in practice). - HTML/XML sniff — if the first bytes start with
<, classify as HTML or XML. Disabled bySettings2.NoHTMLGuess=1. - Shebang fallback for extension-less files (same recognisers as step 2).
- ANSI-art fallback — DOS/OEM-encoded files fall back to the ANSI Art lexer.
- Default schema. Whatever is set as Default Scheme in the Select Scheme dialog (default: Pure Text Files).
The two toggles in the Select Scheme dialog
| Checkbox | INI key | Meaning |
|---|---|---|
| Set as default | [Styles] DefaultScheme=<n> |
The schema used at startup and when nothing else matches. |
| Automatic detection | [Styles] AutoSelect=0|1 |
When off, steps 3–7 above are skipped. File → schema association is based solely on extension. |
Extension lists
Each schema carries a hard-coded default extension list (compiled into Notepad3.exe) plus an optional user override stored in the schema's INI section:
[Python Script]
FileNameExtensions=py;pyw;pyi;setup.py;test_*.py
The override replaces the default; the two are not merged. The field accepts both plain file extensions and filename wildcard patterns in a single semicolon-separated list.
Entry classification
Each entry is one of two kinds — Notepad3 picks based on what characters it contains:
| Entry contains… | Treated as… | Matched against |
|---|---|---|
only letters / digits (no *, ?, .) |
plain extension token | the file's extension (the part after the last ., case-insensitive) |
any of *, ?, or . |
filename wildcard pattern | the bare filename via Win32 PathMatchSpecW |
Examples:
| Entry | Kind | Matches |
|---|---|---|
py |
extension | foo.py, foo.PY |
*.py |
wildcard | any file ending in .py (same as py, just spelled out) |
Makefile* |
wildcard | Makefile, Makefile.dev, Makefile_old |
CMakeLists.txt |
wildcard (literal) | exactly CMakeLists.txt |
??.log |
wildcard | any two-character name + .log |
Makefile |
extension | files with extension Makefile (rare; not the literal extensionless filename) |
Syntax rules
| Aspect | Rule |
|---|---|
| Separator | ; (semicolon) is canonical. A single space also works, and whitespace around ; is tolerated. |
| Case | Both passes are case-insensitive. |
* in a wildcard |
Matches zero or more of any character. |
? in a wildcard |
Matches exactly one character. |
. in a wildcard |
Matches a literal dot — there is no escape character. |
| Buffer limit | 512 characters per schema (counting all entries combined). Longer values are silently truncated when read from the INI; a debug-output warning is emitted. |
Worked examples
; Plain extensions (the common case)
[Python Script]
FileNameExtensions=py;pyw;pyi
; Match the literal extensionless file CMakeLists, plus its .txt sibling
[CMake]
FileNameExtensions=cmake;ctest;cmakelists.txt;CMakeLists*
; Dockerfile family — Dockerfile, Dockerfile.dev, Dockerfile.prod, …
[Docker]
FileNameExtensions=dockerfile;Dockerfile*
; Shell config files starting with a dot
[Bash Script]
FileNameExtensions=sh;bash;.bashrc;.bash_profile;.profile
; Route Python test files to a separate schema
[Python Test]
FileNameExtensions=test_*.py;*_test.py
; Single-character pattern: any 1-char-name file ending in .x
[Custom]
FileNameExtensions=?.x
Common pitfalls:
Makefile(no*,?,.) is treated as a plain extension, so it matches files whose extension isMakefile(i.e.foo.Makefile) — not the literal extensionlessMakefile. To catch the latter, useMakefile*.Makefile*is greedy — it also matchesMakefile_oldorMakefilexxx. If you need exactness, list each name explicitly (Makefile;Makefile.dev) or rely on the.form (Makefile.*) which still matchesMakefile.foobut excludesMakefilexxx.setup.py(entry contains.) is a wildcard literal — it only matches the literal filenamesetup.py, notmysetup.py.
Precedence
Within Style_SetLexerFromFile() the auto-detect pipeline tries the wildcard / filename pass across all schemas first, and only then falls back to the plain-extension pass across all schemas. Within a single schema the two passes are independent: an entry's classification (plain vs wildcard) decides which pass it participates in.
When two schemas claim the same plain extension, or both match a file with their wildcards, the schema that appears first in the internal schema array wins. There is currently no UI to reorder schemas.
Legacy \regex syntax
Earlier versions of Notepad3 used a PCRE2 regex syntax for filename matching, marked by a leading backslash (e.g. \^Makefile$). This has been removed. Existing INIs migrate automatically on load:
- Patterns of the form
\^<name>$where<name>is alphanumeric (with optional_,-, and\.escapes) are translated to a wildcard equivalent. Examples:\^Makefile$→Makefile*,\^cmakelists\.txt$→cmakelists.txt,\^\.bashrc$→.bashrc. - Anything more complex (real regex metacharacters like
.+,[abc],?, missing anchors, etc.) is dropped — translation isn't safe.
In both cases the in-memory list is rewritten on the next save, so user INIs converge to wildcard-only over time. A debug-output line records the migration per schema. Hand-tuned entries that get dropped may need to be rewritten as wildcard patterns.
Editing in Customize Schemes
Select a schema node in the left-hand tree and edit the FileNameExtensions field. On save:
- Empty field (or whitespace only) → the compiled-in default is restored.
- Field equals the default → the INI key is removed entirely so
Notepad3.inistays clean. - Field differs from default → the full value is written back to the schema's section.
Persistence still requires Save Settings On Exit or Settings → Save Settings Now (F7); see §2.
6. The Customize Schemes dialog
- Launch: Menu → View → Customize Schemes… (
IDM_VIEW_SCHEMECONFIG = 41003) or F12. - Modeless — you can keep it open while editing files and see live preview.
Tree
Every schema appears in the left-hand tree. Expand a schema to see its style slots. Selecting:
- a schema node lets you edit the FileNameExtensions field for that schema.
- a style node lets you edit the raw style string plus helpers for font, foreground, and background.
Editing aids
| Control | Role |
|---|---|
| Style text field | Free-form style string — full mini-language from §3. |
| Font… button | Opens the common ChooseFont dialog and writes font:, size:, weight:, italic back into the text field. |
| Foreground / Background | Colour pickers; write fore:#… / back:#… tokens. |
| Default | Resets the current style to its compiled-in default (discards the INI override for that style only). |
| Preview | Toggles live preview of your edits in the main editor. |
| < Prev / Next > | Step through the styles of the current schema. |
| Import… | See §8. |
| Export… | See §8. |
| Dark Mode Highlight Contrast (slider/field) | Adjusts how strongly foreground colours are re-mapped for legibility against the dark background. Stored as Settings.DarkModeHiglightContrast. |
Cancel vs OK
Clicking Cancel reverts every style string that was modified during this dialog session (a backup is taken on open). Closing the window via the title-bar X behaves the same as Cancel.
Drag-and-drop
Dragging a style node onto another style node copies the dragged style's string into the target. Dragging a schema node is blocked (the cursor changes to the no-drop glyph). Drag-and-drop is therefore a quick "use Comments' look for Documentation-Comments" shortcut — not a reordering tool.
7. Themes collection (View → Themes)
A theme is a standalone INI file holding a snapshot of every schema's styles. Themes live in:
<IniFileDir>\themes\*.ini
where <IniFileDir> is the directory that contains the active Notepad3.ini.
Shipped themes
The release package includes three reference themes under Build/themes/:
Dark.iniObsidian.iniSombra.ini
Copy any of them next to your Notepad3.ini (inside a themes folder) and they will appear in View → Themes on next launch.
Menu layout (View → Themes)
Factory Reset ← reloads compiled-in defaults, discards INI/theme
--------------
● Standard Config ← Notepad3.ini itself (always present, always first)
Dark
Obsidian
Sombra
<your-theme>
…
- Factory Reset (
IDM_THEMES_FACTORY_RESET = 37001) discards the current session's style overrides and reloads the compiled-in defaults. It does not delete your INI file, but if you save afterwards, the deltas will be rewritten. - Standard Config (
IDM_THEMES_STD_CFG = 37002) is the internal name for "styles in Notepad3.ini". - Each theme file adds a menu item with consecutive IDs (
STD_CFG + 1,+2, …).
Capacity
The theme table is allocated with 25 slots total — slot 0 for Standard Config, slots 1-24 for theme files. If more than 24 .ini files exist in themes\, the extras are silently ignored (the ones scanned first by FindFirstFile win).
Switching themes
Selecting a theme from the menu triggers this sequence:
- If Save Settings On Exit is on, save the current theme first (to
Notepad3.inifor Standard Config, to the theme file otherwise — withbForceAll = true, i.e. every style written in full). - Reset the INI file cache.
- Load the new theme's
.ini(or the compiled-in defaults for Factory Reset). - Update
Settings.CurrentThemeNameand tick the chosen menu radio.
Settings.CurrentThemeName persists the choice across restarts. If the file disappears before the next launch, Notepad3 falls back to Standard Config.
Creating a new theme
Easiest flow:
- Get your styles looking the way you want (Customize Schemes, regular save).
- View → Customize Schemes… → Export…, save as
<IniFileDir>\themes\MyTheme.ini(see §8). - Restart Notepad3. View → Themes → MyTheme now appears.
8. Export / Import
Both actions are in the Customize Schemes dialog (F12), lower left.
Export
- File dialog filter:
*.inionly. - Written with
bForceAll = true— every style is written out in full, regardless of whether it equals the default. Exported files are therefore self-contained and can be shared with anyone. - Sections written:
[Custom Colors]— 16 palette slots.[Styles]— global settings:Use2ndDefaultStyle,DefaultScheme,AutoSelect,SelectDlgSizeX,SelectDlgSizeY.[Common Base],[2nd Common Base]— super-schemas.- One section per schema (~60 sections).
FileNameExtensionskeys inside each schema section.
Import
- File dialog filter:
*.ini. - Loads the target file into the cache and re-reads style strings out of it. Existing in-memory styles are replaced slot-by-slot for keys present in the imported file; keys missing from the imported file keep their current value.
- Extensions are imported too — your previous per-schema extension customisations will be overwritten by whatever is in the imported file.
- Import does not persist by itself. Until you save settings (or a later auto-save fires), re-starting Notepad3 reverts to the previously persisted state. Hence the usual recipe: Import → verify → Save Settings Now (F7).
Comparison table
| Operation | Scope | What it writes | Uses bForceAll |
|---|---|---|---|
| Save Settings Now (F7), theme = Standard Config | Notepad3.ini |
Only deltas from defaults | ✘ |
| Save Settings Now (F7), theme = named theme | themes/<Name>.ini |
Only deltas from defaults | ✘ |
| View → Themes → with SaveSettings on, leaving a named theme | themes/<oldName>.ini |
Every style | ✔ |
| View → Themes → with SaveSettings on, leaving Standard Config | Notepad3.ini |
Only deltas | ✘ |
| Customize Schemes → Export… | user-chosen .ini |
Every style (self-contained) | ✔ |
9. Anatomy of a style INI file
Whether it is your Notepad3.ini or a shipped theme like Dark.ini, the schema-related sections follow the same layout:
[Custom Colors]
01=#252526
02=#1346CE
03=#A2C5D4
; … up to 16 slots
[Styles]
Use2ndDefaultStyle=0
DefaultScheme=2
AutoSelect=1
SelectDlgSizeX=0
SelectDlgSizeY=0
[Common Base]
Default Style=font:$Code0; size:11; fore:#DEDEDE; back:#1E1E1E
Margins and Line Numbers=font:Consolas; size:-2; fore:#A4FFFF; back:#2B2B2B
Matching Braces (Indicator)=fore:#00FF00; alpha:40; alpha2:220; indic_roundbox
Selection=fore:#000000; back:#3F84DB; alpha:50; alpha2:150; eolfilled
Current Line=back:#323232; alpha:60; alpha2:255
Caret=fore:#FFFFFF; block
; …
[2nd Common Base]
2nd Default Style=font:$Code1; size:10; fore:#DCDCDC; back:#000000
; …
[Text Files]
Default Style=font:$Text0; size:11; fore:#E0E0E0
FileNameExtensions=txt;text;log;asc;doc;diz;nfo
[C/C++ Source Code]
Default=fore:#D7D7D7
Comment=italic; fore:#608B4E
Keyword=bold; fore:#569CD6
Type Keyword=fore:#4EC9B0
String=fore:#CE9178
Number=fore:#B5CEA8
Operator=fore:#B4B4B4
Preprocessor=fore:#C586C0
FileNameExtensions=c;cc;cpp;cxx;h;hh;hpp;hxx;inl
; … one section per schema …
Observations:
- Section names (
[C/C++ Source Code],[Python Script], …) are the schema display names as used internally; they are stable across versions but are localised copies of the names shown in the UI. - Style keys inside a section (
Default,Comment,Keyword,Preprocessor, …) are fixed per schema — you cannot add new ones. - Missing keys fall through to the Common Base → hard-coded default chain.
FileNameExtensionsappears at most once per schema. The list is semicolon-separated; entries beginning with\are regexes matched against the full filename.- Custom Colors entries are just hex colours; they populate the 16-slot picker, nothing more.
Encoding. INI files are written in UTF-8. A UTF-8 BOM is tolerated on read. Do not hand-save as UTF-16 — Notepad3's INI parser is 8-bit.
10. Toolbar themes (separate from colour schemes)
The directories under the project's themes/ folder (Flat, b&w, professional, std_scaled, c6248) are toolbar bitmap themes, not style themes. They contain .bmp files (Toolbar.bmp, ToolbarDisabled.bmp, ToolbarHot.bmp, plus 24- and 48-pixel variants).
The toolbar bitmap in use is chosen via the [Toolbar Images] section in Notepad3.ini — see Configuration.md for details. This mechanism is entirely independent of the schema/theme system described above.
References in the code
For developers wanting to look up behaviour described above:
| Topic | Primary file(s) |
|---|---|
Data structures (EDITLEXER, EDITSTYLE, KEYWORDLIST) |
src/StyleLexers/EditLexer.h |
| Schema array, loading, saving, layering | src/Styles.c — g_pLexArray[], _ReadFromIniCache(), Style_ToIniSection(), Style_CanonicalSectionToIniCache(), Style_ExportToFile() |
| Auto-detection pipeline | src/Styles.c — Style_SetLexerFromFile(), Style_MatchLexer(), Style_WildcardMatchLexer(), Style_SniffShebang() |
| Customize Schemes dialog | src/Styles.c — Style_CustomizeSchemesDlg(), Style_CustomizeSchemesDlgProc() |
| Select Scheme dialog | src/Styles.c — Style_SelectLexerDlg(), Style_SelectLexerDlgProc() |
| Theme menu & switching | src/Styles.c — _FillThemesMenuTable(), Style_InsertThemesMenu(), Style_DynamicThemesMenuCmd(), Style_ImportTheme() |
| Menu command IDs | language/common_res.h — IDM_VIEW_SCHEME, IDM_VIEW_USE2NDDEFAULT, IDM_VIEW_SCHEMECONFIG, IDM_THEMES_STD_CFG, IDM_THEMES_FACTORY_RESET |
| Shipped themes | Build/themes/Dark.ini, Obsidian.ini, Sombra.ini |