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.
- 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
| 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).
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.
| 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).
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.
| 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 |
- 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.
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.
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.
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=1Both base schemas coexist in the INI: toggling only flips which one Notepad3 reads.
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).
| 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. |
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_*.pyThe 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.
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) |
| 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. |
; 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=?.xCommon 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.
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.
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.
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.
- Launch: Menu → View → Customize Schemes… (
IDM_VIEW_SCHEMECONFIG = 41003) or F12. - Modeless — you can keep it open while editing files and see live preview.
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.
| 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. |
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.
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.
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.
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.
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, …).
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).
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.
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.
Both actions are in the Customize Schemes dialog (F12), lower left.
- 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.
- 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).
| 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) | ✔ |
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.
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.
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 |