Журнал изменений

Официальный сайт SLAED CMS

Журнал изменений

Фильтр и поиск

Всего: 1140 Доступных коммитов | Отфильтровано: 1140 Коммиты | Страница: 1 / 114
Вчера (24.08.2026)
Feature: the settings window comes back after the post it starts, and demo/ carries sixteen band treatments
Автор: Eduard Laas | Дата: 23:12 24.08.2026

The settings window posted a row and the browser closed the dialog on the navigation that followed, so a second answer needed the window opened again by hand. It now names itself before the form leaves and comes back on the next load. Alongside it, demo/ carries sixteen prototype treatments of #head-content and #demo-line for the dark mode, where the seasonal photo keeps its full brightness while the page around it turns over.

Core changes:

  1. Settings window (core/admin.php):
  2. One row of the window submits through requestSubmit() with submit() as the fallback

    • requestSubmit() fires the submit event, which is what marks the window as open
    • submit() stays for a browser that does not carry it
  3. The comments over getAdminSettingsRow() say the same thing in fewer words
  4. Window reopening (plugins/system/slaed.js):
  5. A form inside a dialog marks that window in sessionStorage before it navigates

    • Only a window carrying an id is marked, because only such a window can be found again
    • A form driven by htmx swaps in place and is skipped, or it would open a window later
  6. setWindowBack() reads the mark once on the next load, clears it and opens that window
  7. Locale switch note (core/security.php):
  8. The comment over setLangChoice() is shortened; no behaviour change
  9. Band treatments (demo/):
  10. Sixteen self-contained pages: the live band markup, the real theme CSS, one style block each

    • The dial darkens the photo in the dark mode alone; every effect is independent of it
    • Motion runs on transforms, so the compositor carries it instead of the painter
    • The photo moves as its own layer through background-image: inherit, so the five season rules stay in one place
  11. assets/demo.js holds the manifest, the page chrome and the control rail; index.html is the gallery
  12. README.md records how to view them and what the migration into the theme will require

Benefits:

  • An administrator changes two settings in a row without reopening the window between them
  • The candidate treatments can be compared side by side before any of them touches the theme
  • Nothing in demo/ is read by the site: they are prototypes, outside the theme and its gates

Technical notes:

  • sessionStorage carries one key, slaed-window:reopen, written before a navigation and cleared on read
  • demo/ holds literal CSS values by design, which is why it sits outside templates/
  • No schema, URL or API contract changes
Feature: the settings window answers for all three axes, and the language switch stops answering to a link
Автор: Eduard Laas | Дата: 21:15 24.08.2026

The panel settings window landed in 7e864723 with three rows built three ways, one of them posting without a token; the locale switch answered to any GET that carried newlang. This commit closes both, collapses the rows onto one builder, and follows the RSS feed the same way: declared where a reader looks for it, stable where it was random.

Core changes:

  1. Settings window (core/admin.php, templates/admin/fragments/settings-row.html):
  2. getAdminSettingsRow() takes the axis and returns the row

    • getAdminLanguagePick() and getAdminEditorPick() collapse into it
    • the panel stops drawing a mode row of its own, so templates/admin/fragments/
mode-switch.html and its fork entry in tools/ui-contract.php are gone
  • the sl-mode class moves onto settings-row.html behind an is_mode flag, which
keeps --sl-mode-width and --sl-mode-height read and the frozen API intact
  • hidden fields settle on fragments/hidden.html for all three rows
  • the sidebar editor block leaves getAdminInfo(); the window is the only place the editor is offered, and the function loses three globals and gains a return type

  • CSRF on the editor and the locale (admin/index.php, core/security.php, index.php):
  • updateAdminEditor() refuses a request without checkAdminPost('changeeditor')
  • setLang() only reads the cookie now; setLangChoice() does the writing

    • a token check cannot live in setLang(): it runs before the session, the database
and the parser exist, and every admin POST came back as the login page
  • both op=newlang handlers call it and redirect, so the POST turns back into a GET
  • scope newlang joins the token allowlist of checkDynamicMark(), because the site block sits on cacheable pages and needs getPageToken()

  • Language producers (blocks/languages.php, templates/lite/fragments/lang-switch.html):
  • the flags became submit buttons of one form, the select became a POST of the same
  • the geo redirect writes the cookie directly instead of handing itself ?newlang=
  • getAdminLanguageLinks() is gone: the panel renders with the admin theme, whose layout has no admin_langs, so the strip was computed on every load and discarded

  • RSS feed (core/user.php, core/system.php):
  • the site declares its own feed with <link rel="alternate"> in the head
  • <atom:link rel="self"> and dc:creator replace an antispam@antispam.com placeholder
  • ?go=rss without a name answers with the first module instead of a random one
  • Route handler names (admin/index.php, core/system.php, modules/account/index.php):
  • every handler carries a verb prefix now: updateAdminEditor, setAdminThemeMode, setAdminLanguage, deleteAdminSession, getAdminLoginForm, checkAdminLogin, addAdminAccount, and login_report becomes addLoginReport

  • the op strings and token scopes they answer to are untouched
  • Login journal (core/system.php):
  • addLoginReport() reads the flag before the kind becomes a string

    • a non-empty string is true either way, so log_u was unreachable and log_a alone
decided both journals, which wrote user passwords the operator had switched off
  1. Removals and site header (docs/, demo/, templates/lite/partials/site-header.html):
  2. docs/SETTINGS-FIXES-2026.md and demo/ are deleted, no code referenced either
  3. the RSS button leaves the header, browsers have not rendered a feed since 2018; the remaining tones shift back onto a contiguous position % 6 cycle

  4. --sl-wordmark-img-width 145px -> 150px and a quoting fix in ThemeCreationTest.php ride along, both unrelated to the rest and included on request

Benefits:

  • One builder and one fragment for a row instead of three constructions
  • Two handlers that changed state without a token now refuse one that lacks it
  • A locale change no longer happens because a prefetcher followed a link
  • Readers find the feed without being handed its address

Technical notes:

  • Breaking: log_u now means what it says. With log_a on and log_u off, user logins stop being written to log_user.log, and the attempted passwords with them

  • Breaking: RSS <author> becomes <dc:creator>; a parser reading <author> loses the name
  • Breaking: ?newlang= as a GET no longer switches the locale anywhere
  • The site language switch returns to the referring page rather than to the home page
  • No storage, schema or route changed: every op string and token scope is as it was
Docs: the follow-up plan for the settings controls, written from an audit of the commit that landed them
Автор: Eduard Laas | Дата: 15:42 24.08.2026

Seven batches found by reading the colour mode rail and the panel settings window back, not by running into a failure. One of them closes a hole that predates the work; the rest are consolidation, a contrast measurement and two decisions that were left open. The file follows the plan documents already in docs/: a status line, no line numbers, and every reference by the name it is searched for.

Core changes:

  1. Follow-up plan (docs/SETTINGS-FIXES-2026.md):
  2. what already holds, so the next reader does not re-derive it and does not undo it

    • the rail places its knob with :has(), and no state attribute is written from PHP
    • --sl-mode-width and --sl-mode-height stay declared because a frozen API may not lose a name
    • the measured contrast the groove gradient was chosen for
  3. batch 1: changeeditor() writes the session and the administrator row from a POST that carries no token

    • the mode handler beside it is guarded, this one never was, and the window gave it a second caller
    • the language row is left as an open question, because newlang answers to a GET link as well
  4. batches 2 to 6: one builder for the three window rows, the alias the frozen tokens were given, the editor offered in two places, the panel losing two settings without JavaScript, two category tones thin under a white icon

  5. batch 7: the toolbar clip and the missing screenshot pair
  6. what is deliberately not done, so the next pass does not start there

Benefits:

  • the security finding is written down with its scope and its verification instead of living in a chat
  • the two undecided points are named as decisions rather than left to be rediscovered
  • the measured figures travel with the plan, so a repaint knows what it has to beat

Technical notes:

  • no code changes, documentation only
  • the plan is a working document: it is deleted by the batch that finishes it, and anything durable moves to the permanent references under docs/

Feature: the colour mode opens all three answers, and the panel gathers its settings into one window
Автор: Eduard Laas | Дата: 15:30 24.08.2026

The site header stops cycling through the modes one press at a time and stands all three open on a rail; the panel stops spending its toolbar row on six flags and a toggle, and gathers the colour mode, the content editor and the language into one window opened from a single item. Both spend handlers that already existed: no new storage, no new op, and the editor keeps writing the administrator row it always wrote.

Core changes:

  1. Colour mode control (core/system.php, templates/lite/fragments/mode-switch.html):
  2. getThemeModeSwitch() returns the three modes with the one in force marked, instead of the next one to step to

    • the order is light, auto, dark, so the answer that follows the system sits between the two it chooses from
    • the knob rides over the cells and the active cell hands its icon to it, so nothing is drawn twice
  3. the rail places its knob with :has() on the active cell, so no state attribute is written from PHP
  4. Settings window of the panel (core/admin.php, templates/admin/fragments/window-settings.html):
  5. getAdminSettingsWindow() builds one window from three rows, each posting to the handler that already owns it

    • the mode writes its cookie, the editor writes _admins.editor through changeeditor(), the language writes the session
    • Editor::getSelect() is reused as it stands, so the sidebar block and the window stay in step
  6. getLanguageList() is the one list the flag strip of the plain panel and the language row both read
  7. the toolbar keeps one item for the window and rules the row where the kind of destination changes
  8. Theme components (templates//assets/css/.css, tools/ui-contract.php):
  9. .sl-mode-rail, .sl-mode-cell and .sl-mode-knob replace the single .sl-mode-but of the site
  10. .sl-set-row and .sl-set-item carry the window rows and the toolbar item; the component set is declared in the contract
  11. a toolbar item may now hold a button as well as a link, which is what lets the window open from the menu row
  12. the social row takes its hover ground from the category palette instead of eight hand-picked colours

Benefits:

  • any mode is one press away on the site, where three presses were needed to come back to the system answer
  • the panel toolbar returns to one line: 40px against the 70px the flags and the toggle wrapped it to
  • one place for the three answers an administrator changes from any screen, with room for a fourth
  • eight colour rules fall to one rotation the tree already spends on categories

Technical notes:

  • no schema change and no new op: every row posts to an existing handler
  • fragments/mode-switch.html diverges by theme on purpose, with the reason recorded in tools/ui-contract.php
  • --sl-mode-width and --sl-mode-height stay declared in both themes, as a frozen API may not lose a name
  • demo/ carries the stand the variants were chosen on; it links the shipped CSS and JS and ships no theme of its own
Эта неделя (21.08.2026)
Fix: the commit hook would have been silent on a second machine, in three separate ways
Автор: Eduard Laas | Дата: 12:22 21.08.2026

A hook that is present but never runs is worse than no hook: it reads as covered. On any clone other than the one it was written in, this one was present and silent for three unrelated reasons, and on a machine without composer it would have refused every commit without saying why.

Core changes:

  1. The hook is enabled per machine, not per repository (package.json):
  2. core.hooksPath lives in .git/config, which neither a clone nor a pull carries

    • git will not let a repository enable its own hooks, which is a security property
and not an oversight, so something has to set it on each machine
  • postinstall sets it during npm install, and npm run ui:hooks sets it by hand
  • the postinstall falls through with a message outside a git checkout instead of
failing the install
  1. The hook file was committed without its executable bit (tools/hooks/pre-commit):
  2. core.fileMode is false in this checkout, so chmod +x never reached the index and the file went in as 100644

  3. Windows does not care - git runs hooks through sh either way - but git on Linux and macOS skips a non-executable hook without a word

  4. git update-index --chmod=+x puts it in as 100755
  5. The hook assumed two tools that a fresh machine does not have (tools/hooks/pre-commit):
  6. php has to be on the PATH of the shell git runs hooks in
  7. vendor/ is gitignored, so phpunit is absent until composer install
  8. both are checked before anything else and each stops with its own reason and the remedy, rather than failing on a confusing error or stepping aside quietly

  9. verified in all four directions: no php stops with the PATH message, no vendor stops with the composer message, SLAED_SKIP_GATES=1 passes silently, and a normal tree prints [gates] ok

  10. Documentation (CONTRIBUTING.md, docs/TESTS.md):
  11. both said "enable it once per clone", which reads as something the clone does
  12. both now say it is per machine, name core.hooksPath and .git/config as the reason a clone and a pull leave it behind, and record what the hook refuses to run without

Benefits:

  • the hook either runs or says why it did not, on any machine
  • npm install is enough to arm it after a clone

Technical notes:

  • this commit carries no file the gates watch, so the hook exits early on it by design; the fast set was run by hand instead and is green

  • a gate that steps aside when its tools are missing is the failure this repository has now closed five times, which is why both new checks refuse rather than skip

Chore: the theme gates start running themselves, and four of them stop answering when their input is gone
Автор: Eduard Laas | Дата: 12:11 21.08.2026

The etalon plan is finished, so its prose moves into the reference documentation and the plan file goes. What replaces the checklist nobody would remember is a pre-commit hook for the cheap gates and a two-word pair for the visual one. Four gates that read zero while checking nothing are closed, and the committed screenshot baseline is dropped because it cannot be stable on a stand whose own content moves.

Core changes:

  1. Gates that no longer answer when their input vanishes:
  2. tools/ui-contrast.json could be deleted and the contrast count still read zero

    • getContract() folds the registry in only if (is_file(...)), so a missing file
left nothing to check and ThemeContractTest stayed green
  • ThemeContractTest now asserts the file exists and carries pairs for every theme
  • node tools/ui-shots.mjs --check wrote a missing baseline image instead of failing

    • a check whose baseline is absent re-baselined itself against the tree it was
asked to judge and exited zero; it reports MISSING and exits non-zero now
  • php tools/ui-audit.php --markup had no limit beside the count

    • markup.limit is 0, so the scan fails on the next hardcoded class, style or tag
  • the screenshot rig captured a degraded set without credentials

    • --before and --after refuse to start without SLAED_UI_USER and SLAED_UI_PASS,
because a run without them skips every state that needs a session
  1. Automation (tools/hooks/pre-commit, package.json):
  2. the pre-commit hook runs the audit, the markup scan and the theme, template and parser tests on every commit carrying a file able to move a count

    • about twenty seconds, and nothing at all on a documentation commit
    • enable once per clone with npm run ui:hooks; SLAED_SKIP_GATES=1 skips one
    • when a commit touches theme CSS or a canon template it prints the two visual
commands, because no count can see a moved pixel
  • npm run ui:before and npm run ui:after are the visual gate

    • ui:after empties the page and template caches, compares against the capture
`ui:before` took, regenerates the contrast registry when a base.css moved, reads
the counts and prints one verdict
  • verified in both directions: no change gives PASS, a padding shift on a class the
page really carries gives eight DIFFs and FAIL
  1. The screenshot baseline is no longer committed (tools/ui-baseline, .gitignore):
  2. measured: a check against the committed set went red hours after capture on a tree whose frontend was byte-identical - 100% size changed at sm, md and lg on the front page and the forum list, 1.6% to 3.9% even at xl

  3. the page height follows the content of the stand, and a mask cannot save a box whose height is what moves, so the reference is captured per change into a temp directory

  4. 168 images and about 80 MB leave the working tree; the path is gitignored so a stray capture cannot put them back

  5. A scratch theme stopped leaking its compiled templates (tests/Support/theme_scratch.php):
  6. deleteScratchTheme() removed templates/scratch-* and left storage/cache/templates/scratch-* behind, so every phpunit run and every --newtheme leaked about half a megabyte under a name nothing can read again

  7. Documentation (docs/, root):
  8. docs/THEME-ETALON-2026.md is deleted; its contract, its gate table and its measured findings move into docs/TEMPLATES.md as three reference sections, without the batch narrative and without any superseded figure

  9. docs/PARSER.md records that the parser owns no markup and needs a global $tpl
  10. docs/ARCHITECTURE.md records that $tpl is created before $prs because the parser depends on it, not by coincidence

  11. docs/EDITORS.md records that every driver mounts through fragments/editor-mount.html
  12. CONTRIBUTING.md, UPGRADING.md and docs/TEMPLATES.md stop describing HTML leaving PHP as work in progress; it is done and a gate holds it

  13. five files that pointed at the deleted plan now point at docs/TEMPLATES.md

Benefits:

  • the cheap gates need nothing remembered: they run on commit or refuse it
  • a gate whose input is missing now fails instead of reporting a clean zero
  • the working tree drops from about 145 MB to 65 MB

Technical notes:

  • .git is unchanged in size: the baseline blobs stay in history and any image can be recovered with git show b115eaa0:tools/ui-baseline/<name>.png

  • the hook needs core.hooksPath pointed at tools/hooks, which npm run ui:hooks does
  • the full 168-image walk has not been run under the new --before/--after flags; the cycle was verified with --only=voting, and the contrast branch of ui:after has not executed because no palette moved in that test

  • no breaking change: 915 tests pass, phpstan is clean, every ratcheted count holds
Refactor: the last markup leaves PHP, and a panel painting with names no theme has is found
Автор: Eduard Laas | Дата: 10:03 21.08.2026

A theme cannot restyle what PHP hardcodes, so the closing batch of the etalon plan moves every class attribute, inline style and HTML tag out of PHP and into the theme layer. The scan that measures it falls from 143 occurrences in 21 files to zero and gains a limit, so the next one fails the build instead of printing a number.

Core changes:

  1. Extended statistics panel (admin/modules/statistic.php):
  2. The inline <style> block it printed painted with --line, --bg, --soft, --muted, --blue2, --green, --purple and --orange

    • None of those eight names exists in either theme, so every border, background,
shadow and accent in the panel resolved to nothing and it had never been styled
  • No count could see it: the audit reads CSS files and this was a string in PHP
  • The markup is partials/statistic-extended.html and the CSS is 24 rules in theme.css

    • Every value lands on a ladder; the four accents are primary, success, warning
and accent; five component tokens are declared under statx
  • These eight images are the only ones of 168 that moved, and they moved on purpose
  1. Monitor traffic chart (admin/modules/monitor.php):
  2. The 900x285 SVG becomes partials/monitor-chart.html taking four series, six hover slots and two axis lists

  3. Four attribute kinds are dropped as dead

    • stroke and filter on lines and areas are SVG presentation attributes, which lose
to any CSS rule, and .sl-chart-line-up { stroke: ... } had been winning already
  • .sl-chart-tip .muted set fill: var(--sl-text), which .sl-chart-tip text two rules
above already set, so the rule and the class are both gone
  1. Markdown and BBCode renderer (core/classes/parser.php):
  2. 55 tags, 7 inline styles and 4 classes move into four new fragments plus the existing list, list-item, code-highlight, blockquote, div and parser-image

  3. A fragment can own a regular expression's replacement string once it renders the placeholder

    • parser-inline.html rendered with content_html set to the literal $1 gives the
template a whole element and PHP a replacement string
  • A pair that wraps text the parser has not finished reading renders the same whole
element with \x01 as its content and is split there, so a theme never spells half
a tag
  • The blockquote and attachment fallbacks are gone: the parser renders through the theme or not at all

  • One decision, one address, across eighteen smaller files:
  • class strings inside form_attr become semantic flags in form-add.html and form-wrap.html

  • style="display:none" becomes the hidden attribute, which the reset gives !important so nothing a component sets can put back what the page took off screen; the tab script writes .hidden instead of style.display

  • Eight sites that joined text with a tag now call getTplLines() over fragments/lines.html
  • add_voting() built class="sl-field sl-form-control" into select_attr, where select.html had already written class="sl-select..."; an HTML parser keeps the first attribute of a name, so the string had never reached a pixel and is deleted with its parameter

  • The captcha directory guard copies storage/index.html instead of building a document
  • Gates (tools/ui-contract.php, tests/Unit/ParserFixturesTest.php):
  • markup.limit is 0, so php tools/ui-audit.php --markup exits non-zero on the next hardcoded class, style or tag

  • ParserFixturesTest asserts the exact HTML of 86 cases and ran with no engine, so every expected string was what PHP concatenated; it builds a Template now, because the bytes it asserts are a theme's answer

  • tools/ui-contrast.json is regenerated beside the manifest that feeds it: 438 pairs, 118 admin and 320 lite, contrast at zero in both

Benefits:

  • A theme can restyle every element the CMS emits, which is the goal of the plan expressed as a measurement: markup 0, and every count either theme controls at zero

  • The statistics panel is readable for the first time, in both light and dark
  • Three gates that were reporting stale answers now report current ones

Technical notes:

  • Six fragments are byte-identical in both themes: lines, editor-mount, parser-block, parser-inline, parser-link, parser-table; two partials are the panel's alone

  • The parser now hard-depends on the theme layer; there is no plain-HTML fallback left
  • The parser disk cache already carries getTheme() and the class mtime in its key, so theme-owned markup cannot be served across themes or across this change

  • Masking an element for the screenshot gate removes it from the contrast gate, because the collector skips visibility:hidden and display:none; the registry shrank from 460 pairs to 438 for that reason and not from a regression, which the retired span.sl-but.sl-hidden still sitting in the committed file independently proves

  • No breaking change: 86 byte-exact parser fixtures pass unchanged, and 160 of 168 baseline images are identical to the pristine tree

Feature: the API freezes, and the last of canon answers for itself
Автор: Eduard Laas | Дата: 01:24 21.08.2026

Batch 8 of docs/THEME-ETALON-2026.md closes the remainder of canon and freezes the theme API. Every shared name the two etalons spell differently is now either one file or an entry with a written reason, and the freeze is held by a gate rather than by a sentence: the baseline carries a roster of the 297 and 404 names each API block declares, the audit compares it every run and --store refuses to write while a name has gone missing.

Core changes:

  1. The 28 same-named templates of canon (tools/ui-contract.php, tools/ui-audit.php):
  2. --cross reads a shared template the way it already read a shared selector

    • templates carries a written reason per divergence, canon names the two
directories the demand applies to, and unanswered ones join the global `cross`
  • layouts and pages are reported and demand nothing: the page shells differ by nature
  • two of the 28 were one contract with two spellings and are now one file

    • radio.html: admin's is lite's plus four optional keys no lite caller passes
    • changelog-body.html: the admin h3 skipped a level under the h1 of module-head,
so both are h2 and the admin rule moved with it at the same --sl-font-h4
  • the other 26 are two contracts under one name, which the key sets show rather than the line counts: link maps twelve names in the panel against thirty-three on the site with not one in both, inline-badge fourteen against twenty, span one against 22

  • One name per intent for hiding and dimming (both themes, core/user.php, modules/forum/index.php):

  • .sl-hidden meant display: none in admin and an opacity step in lite

    • read by producer rather than by class: six lite call sites mean gone, four mean
closed but still readable - a comment awaiting approval, an unpublished forum
post, an opened message, an action the reader may not take
  • those four take .sl-dimmed, which both themes already carried
  • .sl-none and .sl-hidden were two spellings of display: none, so the name that says the role keeps the intent and .sl-none is retired from both themes

    • the admin is_hidden branch of span.html had no producer at all, and the
!important that served it goes with it
  1. The focus ring of a link (both base.css):
  2. a { outline: none } was in both themes, not one: in admin it sits in the group input, textarea, select, a, which is why the tool reported only lite

  3. neither theme drew a:focus-visible in its place, so both take the ring the rest of each theme already draws, spelled identically

  4. The !important flags, read once (both theme.css):
  5. three in lite were fighting nothing: a hover rule that already outscored its target, an id selector nothing in the file can reach, and a dead rule whose class no template or script writes

  6. every one that remains carries the reason beside it - the glyph face beating an element font, the editor engine writing its height inline, a print rule reaching past the inline display the tab script writes

  7. The HTTP half of the theme-creation gate (tools/ui-shots.mjs, tests/Support/theme_scratch.php, tests/Support/theme_probe.php):

  8. node tools/ui-shots.mjs --newtheme builds a scratch copy of an etalon, points the account the rig signs in as at it, renders every frontend page of the manifest and restores the column and removes the tree in finally

  9. both halves build that copy through one lifecycle, theme_scratch.php, so a lifecycle spelled twice cannot drift into two

  10. the three site logs are read before and after the walk: a theme that renders a page and writes a notice behind it has still failed

  11. The freeze (tools/ui-contract.php, tools/ui-audit.php, tests/Unit/ThemeContractTest.php):

  12. frozen is true, and the roster under api in the baseline is what holds it
  13. an addition wrongs no copy; a rename in place is refused by the run, by --store and by phpunit alike

  14. The rig learns two pages it could not hold still (tools/ui-shots.json):
  15. admin-monitor grew 6954 to 7092 to 7557 pixels in the seven seconds after load: two panels arrive on load and are then replaced out of band by a four-second poller, and what moved was their height, so masking could never have held them

  16. the bar of the statistic page was masked and the count and per cent printed beside it were not, so the page flapped around the threshold on a walk long enough for the rig's own visits to move the figures

Benefits:

  • every shared name across the two etalons is now answered for, by one file or by one written reason, which is what hundreds of copies will inherit

  • one name for gone and one for dimmed, in both themes, with no name left holding two intents at the moment the API is frozen

  • a link that is focused says so on both sides of the CMS
  • creating a theme is checked as a file and as a served page, through one lifecycle
  • important falls 23 to 22 in admin and 17 to 13 in lite, and what is left says why

Technical notes:

  • --sl-font-hero reads 38px: the top step of the font-size ladder holds a different value, with the step count and the role names unchanged

  • probe_boot.php answers 404 and exits unless PHP_SAPI is cli. The probes are served by a stand, register_argc_argv turns a query string into $argv, and theme_probe.php stopped being read-only in this commit

  • tools/ui-baseline is re-captured whole and checked against itself: 168 of 168 states match and every noise floor sits below 0.2%

  • no breaking change to any runtime contract: the template keys that moved are is_hidden to is_dimmed at four call sites, all inside this repository

Эта неделя (20.08.2026)
Refactor: the two themes start answering to each other, and three gates learn what they were counting
Автор: Eduard Laas | Дата: 22:36 20.08.2026

Batch 8 of docs/THEME-ETALON-2026.md. Canon reconciliation is what makes the two shipped themes etalons rather than two codebases with matching names, and the checks that were meant to drive it were adding unlike populations together: one figure of 424 duplicates and one of 135 divergences is answered by grinding rather than by thinking. Split into what they actually measure, they left 50 duplicate groups and 46 divergences - sets small enough to decide one by one.

Core changes:

  1. Cross-theme divergence becomes a gate (tools/ui-audit.php, tools/ui-contract.php):
  2. checkCrossTheme() reads a shared selector two ways

    • same property set, different values: legal, one canon carrying many skins
    • different property sets: needs a written reason under divergent
  3. 135 divergences are now 117 = 71 in value alone and 46 in structure, all explained
  4. the element reset was settled structurally instead of registered: h1..h5, hr, ol, fieldset, a:hover and the two check inputs hold one property set per theme

  5. The duplicate check stops counting need as debt (tools/ui-audit.php):
  6. a body of one declaration is need, the way display: flex under 122 containers is
  7. a body met in two files cannot be merged: CSS has no cross-file selector list
  8. of the 50 groups left, 7 merged, 1 redundant rule deleted, 34 given a reason
  9. dup 178 and 246 -> 0 and 0
  10. A theme is created and checked by a test (tests/Unit/ThemeCreationTest.php):
  11. copies an etalon, repaints only the API block, asks the whole contract of the copy
  12. contrast is measured on the real pair registry put through the same repaint
  13. checkThemeAssets() answered by tests/Support/theme_probe.php on the live core
  14. the skeleton is checked against both gates that demand it, never against itself
  15. The contrast walk composites instead of passing through (tools/ui-shots.mjs):
  16. every sheer layer is composited back down onto the ground that really paints
  17. background-image is walked layer by layer, back to front, not stop by stop
  18. it found the admin footer copyright at 4.26:1 under its own decorative hatch; the hatch alpha is now what the ratio allows and the pair reads 4.60:1

  19. registry 442 -> 460 pairs, both halves at zero below AA
  20. Dark is guarded by pixels (tools/ui-shots.json, tools/ui-baseline):
  21. modes gains dark: 168 committed images, every noise floor under the threshold
  22. --out= sends a capture outside the repository, so a batch can compare its own two captures instead of a committed baseline the stand's own data has moved under

  23. the site counters the profile and the statistic page print are masked
  24. The two names nothing wrote are gone (plugins/system/slaed.js, both themes):
  25. placeFloat() writes --sl-d-float-top and --sl-d-float-left; the theme owns the rule
  26. six inline layout properties and a hardcoded z-index leave the script
  27. unmet 2 -> 0 in both themes
  28. Three zoos in admin take the shape lite had already settled (templates/admin):
  29. the alert reads one --sl-alert-tint and its glyph the base tone; the last raw hex goes
  30. the loading dots read the API block and the tone directly
  31. the switch spells its four aliased tokens and draws the theme's one focus ring
  32. scoped 84 -> 54, --sl-danger-subtle deleted, --sl-knob-dur folded onto the ladder
  33. error.html on the settled names, and four spellings of one intent closed:
  34. 60 tokens declared and 60 read, closed on itself
  35. ::before/::after at 57 sites, "bootstrap-icons" at seven, --sl-radius-circle at 28

Benefits:

  • every count either theme controls reads zero, and cross reads zero beside them
  • a new theme is now provably one copy and one repaint, asserted rather than claimed
  • the dark half of both themes is guarded by images and not only by ratios
  • a contrast failure nobody could see is fixed, and the walk that finds its kind is honest

Technical notes:

  • ui-audit gains isClassUsed(), whose boundary is the name alphabet: a class emitted after a template tag was invisible to it, which read as five dead classes in lite

  • nine classes really were dead and are deleted, with --sl-move-height beside them
  • admin ul keeps its longhands on purpose: a shorthand resets every longhand it omits, which flattened 82 nested list marks when it was written as one

  • ten lite templates carried CRLF in the working tree, which .gitattributes normalises on the way into the index: four template divergences the tool reported were local drift and never in the repository, which is why this commit shows none of them

  • fixtures cover every classifier change: classuse.html, dup-need.css
  • no theme file below the marker names a mode; frozen stays false while canon is open
Docs: the two questions batch 7 left about the rig are written down where they will be found
Автор: Eduard Laas | Дата: 18:11 20.08.2026

Both are about what the gates can see rather than about either theme, and neither blocks the freeze. They are recorded in the risk that names them, in the description of the walk that would change, and as a step of batch 8 that has to answer them.

Core changes:

  1. The dark-mode risk carries what batch 7 measured (docs/THEME-ETALON-2026.md):
  2. Two dark breaks shipped green through every gate and were found by looking

    • The headline over the photo band flipped from white to near-black, because the band
is a photograph with no dark variant while --sl-on-solid turns over
  • The footer wordmark vanished when the band under it inverted, because the wordmark
is an image with white baked into it
  • Both pass AA in both modes, so no contrast pair could report either
  • Until modes gains dark, a batch that touches colour opens the pages in dark, looks at them, and says in its report that it did

  • The walk's sheer-fill reading is named as a half answer (docs/THEME-ETALON-2026.md):
  • Passing through a fill under 0.5 alpha removed exactly two false readings in batch 7
  • It also leaves the honest ones unmeasured: white text on a twenty per cent veil over a photograph now reports nothing rather than something wrong

  • Compositing is the whole answer, about twenty lines in getContrastPairs(), and needs both halves of the registry re-generated in one run

  • Batch 8 gains the step that decides them (docs/THEME-ETALON-2026.md):
  • The cost of each is stated: 168 images and about 72 MB against the only gate that would have caught either break; twenty lines against a registry that may surface new pairs

Benefits:

  • A question asked once is not asked again from scratch, and its cost is already priced
  • The next colour batch knows it owes a look in dark until the manifest covers it

Technical notes:

  • Documentation only; no code, no theme and no gate changes

Всего: 1140 на 114 страницах по 10 на каждой странице

1 2 3 4 5 6 7 8 9 10 114
Хотите опробовать SLAED CMS в действии?
Идеи и предложения
Обратная связь
Подтверждение

Поделиться
QR-код