Traced against the running site (docker theme at AKSRT-theme) with real
computed styles:
- theme_main_radius defaulted to 12, so --main-radius was 12px and every
dropdown panel, card and button was rounder than the reference, which
sets 8px. Default is now 8 in the runtime reader and both schemas.
- The public bundle loads after main.min.css. Its bare img rule replaced
the inline image baseline the menu relies on, and html{overflow-x} made
the root a clipping context that crops the fixed senior mega menu.
Reset is now scoped to content; the horizontal-overflow guard is on body.
- The senior mega menu renders a .container inside the dropdown where the
reference keeps Bootstrap's 15px gutter; the padding-free site-shell
.container rule stripped it, running the card/link grid to the edges.
Rebuilt public.css / public.min.css and the admin SPA.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The public bundle loads after main.min.css, and its bare-element rules
won on equal specificity:
- img { display: block; height: auto } replaced the inline image baseline
the menu relies on (Bootstrap img{vertical-align:middle} + main.css
img{max-height:100%}). Menu glyphs and graphic-card thumbnails were
pulled out of the inline flow, breaking row height and baseline.
Scoped the reset to content and restored the inline baseline inside
.header / .mobile-header.
- html { overflow-x: clip/hidden } made the root a clipping context,
which crops the position:fixed senior mega menu and its drop panels.
The horizontal-overflow guard now lives on body only.
Rebuilt public.css / public.min.css.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The article body ran two highlighting pipelines at once: the runtime
EnlighterJS pass (same one the comment area uses) plus a bespoke
precode.js enhancer, and a hand-rolled .code-block skin. The two
disagreed, so rendered blocks carried a dark #282c34 card with 15px
padding, 14px text, a border and a second copy button stacked on top of
the EnlighterJS toolbar.
Drop the parallel path and let one pipeline own code blocks:
- delete precode.js, single-post/code-blocks.css and the unused
assets/js/enlighter copy that only precode.js referenced
- drop the now-dead has_code requirement (PHP scan + feature enqueue)
- render pre exactly as the reference does: bootstrap's pre box
(9.5px padding / 13px / radius 4) plus main.css's muted background,
with the EnlighterJS theme supplying the skin
Also scope the inline-code skin to :not(pre code) so it stops painting
the block background pink, and remap the toolbar copy glyph: \f0ea is
the clipboard in FA4 but "paste" in FA5; the clipboard is \f328.
Verified against the reference at 1440px both themes: pre 13px/18.57,
9.5px padding, radius 4, muted background; EnlighterJS block heights
151.31 / 60.38 match exactly.
The top-bar theme switch was bound twice — core/theme.js attached a click
handler to #theme-toggle-desktop while runtime/main.js delegated click on
.toggle-theme, and both ship in main.min.js, so one press toggled twice and
the button looked dead.
Also realign the control with the reference:
- Drop the far modifier from fa-toggle-theme so the sun/moon glyphs resolve
against the Solid face at the reference weight instead of Regular.
- Restore .toggle-radius .fa/.icon to the reference form (left/right 0 +
auto margin only). The extra position/top/transform/size/flex block and
the > i selector were overriding the shared abs-* centering rule.
- Replace the per-ID bindings with one delegated listener so the floating
rail, which carries no id, keeps working.
- Remove dead theme.js queries for selectors no markup or stylesheet ships
(#theme-toggle, #dropdown-theme-toggle, .theme-icon-moon/.theme-icon-sun,
[data-action='toggle-theme'], .is-dark).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The legacy .jb-* compat layer in core/buttons.css restated .jb-yellow as #f99d4d -> #f7631d, and because the public bundle loads after main.min.css that value overrode the reference-accurate #f59f54 -> #ff6922 gradient, so the check-in bookmark (and every other .jb-yellow surface) painted the wrong orange. Correct the gradient and rebuild the public bundle.
Also drop the dead is-done class and the attribute-less span wrapper from the signed card badge, which the reference theme does not have. The header dropdown variant keeps its class, locked by the navigation contract.
Make --radius-avatar the single source of truth for frontend user avatar shape. A new primitive in components-ext.css (img.avatar / avatar-mini / avatar-md / avatar-lg / .avatar-img img) forces a circle on every avatar image, which covers the bare avatar-mini in the ban and report modals, the classless img inside the VIP modal's .avatar-img, and the post-card meta avatar that previously depended on post-card.css being enqueued. The user center header frame, the public user home hero and the friends page avatar switch to the same token because route CSS loads after the public bundle and would otherwise override the primitive.
The VIP icon no longer rides the avatar corner. The user card widget, the after-article author card and the sign-in modal stop opting into aksrt_get_avatar_box()'s corner badge, and the icon renders first in the badge/level row instead, since aksrt_get_user_name already orders name -> VIP -> auth -> medal -> level. The sign-in modal appends it after the display name and keeps the legacy precedence where a ban badge takes the VIP slot. The user card widget name row now shrinks its display name so the extra icon cannot overflow a narrow sidebar.
Verified with the new frontend-avatar-shape-contract test plus post-visual, user-card-runtime, circular-button-icon-margin, user-features and page-display contracts, and php -l on every changed PHP file. The public bundle was rebuilt with tools/build-public-assets.mjs.
Clicking 加载更多 in the 多栏目文章 widget showed no skeleton at all — the
list simply stayed still until the response arrived, then the new cards
appeared. The homepage stream, archives and comments already had a working
skeleton; the widget's AJAX load goes through a different client and never
found its loader.
Root cause was DOM placement, not visibility: ContentRenderer emitted
.post_ajax_loader as a *sibling* of .widget-ajaxpager, while the widget REST
client resolves it with container.querySelector('.post_ajax_loader'). The
node existed but could never match, so .is-loading was never added and the
skeleton stayed display:none. Inactive tab panes had the same shape, and the
direct-load branch of ajaxpager() emitted no loader at all.
Changes, all following the reference theme's post_ajax() contract in
zibll/js/main.js (beforeSend anchors the loader to the end of the list;
success only hides it, never removes it):
- ContentRenderer: nest the loader inside .widget-ajaxpager for both the
direct-load and inactive-tab branches, and drop the sibling append.
- widgets/frontend.js: anchorLoader() moves the loader to the end of the
list before .ajax-pag on every request; appended cards are inserted before
it; a replace preserves the node instead of wiping it with innerHTML; a
fallback loader is created when the DOM has none; loadItems() now returns
its promise; drop the unreachable is-loading auto-load branch.
- Message centre, user-home panels and the search page switch from spinner /
opacity feedback to the same .posts-item-shaped skeleton, so all AJAX
surfaces share one loading contract.
- Remove the second, never-wired skeleton system (.aksrt-skeleton-* in
core-ui.css) and the dead spinner stylesheets/rules (skeleton.css,
ajax-spinner.css, .load-more__*, --skeleton-base/--skeleton-shine) plus the
unregistered assets/js/waterfall.js.
- Extend the widget, message-centre, user-home and asset-ledger contract
tests to pin the loader inside the container, the class-only visibility
switch, and the absence of the deleted dead code.
Verified: 470 node tests, 136 PHP widget assertions, admin-contract and
widget-system audits all green; rebuilt public assets after the CSS/JS
removals.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Set --text-base to 0.875rem to match the reference bootstrap body
metrics; em-based reference components (.img-badge, em utilities) now
render at reference scale site-wide. Compensate .btn-lg with
--text-md (18px, bootstrap btn-lg) to keep the size hierarchy. Simplify
the user-card badge rule to the line-height pin only.
Slide-mode images rendered at intrinsic ratio, top-aligned inside the
swiper chain (--pc-height:auto left .swiper-wrapper content-sized), so
the taller box showed a gap at the bottom and never center-cropped like
the image cover. The video cover had the same problem through
video-player.js (height:auto, capped at 70vh).
- aksrt_render_slider scale mode emits --pc-height/--m-height 100% so
the wrapper/slide/img chain fills the ratio box and object-fit: cover
center-crops inside it (same pattern as the .scale-height widgets)
- video-player.js renders the video at height:100% + object-fit:cover;
with an undimensioned container height:100% degrades to auto,
matching the previous behavior
- drop the dead .single-cover padding-bottom rules (every producer owns
its height inline, and the rule squeezed the absolutely positioned
slide inner) and the orphaned --single-cover-scale var in
tokens.css/compat-vars.css/main.css/InlineStyleBuilder
- rebuild public bundles; extend the single-cover-height contract
Reference contract: widget cards carry their own 15px padding and
.widget-content is bare. The added 20px padding inset every widget
card from the content max width on the fixed homepage (the only
bundle loading sidebar.css).
- drop the root padding rule and the unreferenced --flush modifier
- drop the now-redundant user-widget padding:0 workaround
- drop the dead --card-padding-lg token
- lock the contract with negative assertions in the display test
.aksrt-wave was injected into .but without position:absolute, so the
oversized span expanded header publish/VIP buttons on click. Make the
wave absolute, honor .nowave, and skip navbar CTA roots.
Center FA icons in toggle-radius/btn chips, restore menu-item title flex baseline, and match CTA/logo vertical alignment so top bar text and icons no longer drift.