Conversation
gem("bootstrap-sass") -> gem("bootstrap", "~> 4.6"), pulling in
popper_js as a dependency. Re-vendored the JS via
`bin/importmap pin bootstrap@4.6.2` (explicit version -- the unpinned
form resolves to BS5, which drops jQuery and would break every
.collapse()/.modal()/.tooltip() jQuery plugin call in the app's
Stimulus controllers); popper.js is pinned alongside it since BS4's
JS imports it directly.
mushroom_observer.scss now imports mo/map_theme_to_bootstrap4.scss
(BS4-native variable names) ahead of `@import "bootstrap"`, in
addition to -- not instead of -- the existing
mo/map_theme_vars_to_bootstrap_vars.scss: ~20 stylesheets in the app
read bootstrap-sass-named variables directly (`$btn-default-color`,
`$navbar-inverse-bg`, etc.), so removing that file breaks compilation
until those call sites are swept to BS4-native names or mixins.
Fixed two bugs in the BS4 mapping file: $gray-100/$gray-900 were
hardcoded to $LIGHT_COLOR/$DARK_COLOR regardless of theme, backwards
for a dark theme like Amanita -- now derived from
$BODY_BG_COLOR/$BODY_TEXT_COLOR, which already resolve light/dark per
theme. $card-cap-bg used darken() instead of the project's
scale-color() convention.
Deleted _utilities.scss's font-weight-bold/border-top/border-bottom/
list-group-flush rules, duplicates of what BS4 already provides
(cleared for deletion by #5340).
Issue #5348.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
Cross-checked all 107 variables the file sets against the installed bootstrap 4.6.2 gem's _variables.scss. These two don't exist in BS4 -- .btn-link reads $link-color/$link-hover-color directly (confirmed in the gem's _buttons.scss). Leftover bootstrap-sass variable names; harmless as dead assignments but not doing anything. Issue #5348. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
.in -> .show (type-switch, form-images, Components::Collapsible's server-rendered class), .open -> .show (autocompleter dropdown -- moved to pulldownTarget, not wrapTarget: BS4's .dropdown-menu.show is a compound selector on the menu itself, unlike BS3's .open > .dropdown-menu parent-child selector). Deleted matrix-table_controller.js and name-panels_controller.js: both existed only to manually equalize panel heights and insert clearfix spacers for BS3's float-based .row -- BS4's .row is display:flex, which does both natively. Deleted their supporting wiring (MediaQueryTests, the breakpoint-detection sentinel matrix-table_controller.js depended on; the name-panels-target data attrs on ClassificationPanel/LifeformPanel). Deleted collapse-fallback_controller.js: a code comment there already recorded that BS4's collapse data-API calls preventDefault() unconditionally, making the controller's one job redundant. Migrated every $screen-*-min/$screen-*-max breakpoint reference (_carousel.scss, _layout.scss, _lightgallery.scss, _links_buttons_alerts.scss, _matrix_box.scss, _utilities.scss) to BS4's breakpoint-min()/breakpoint-max() functions against $grid-breakpoints, set in mo/map_theme_to_bootstrap4.scss (BS4 stock xs/sm/md/lg, xl widened to 1320px -- MO's custom "lg" value, moved to the tier that is now the top one). Components::Column's grid-width props (sm:/md:/lg:/xl:) and its show_at:/hide_at: visibility classes both key off this same map now, resolving the mismatch a comment on the component flagged: they share breakpoint names and ran on different pixel thresholds by design (grid on BS3 values, visibility pinned to match); now both read the one map, so a given name means one width everywhere. Added the .d-xl-* block to _utilities.scss, which had no BS3 counterpart (xl was not a bootstrap-sass breakpoint). Issue #5348. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
…s BS4 Every remaining bootstrap-sass-only variable name MO's stylesheets referenced directly (button/navbar/panel/alert/dropdown/input color and spacing names, the generic padding scale, $text-color, etc.) is migrated to its BS4-native equivalent or, where BS4 has no equivalent concept, to a direct expression or MO's theme variable: - Panels -> cards: $panel-bg/$panel-default-heading-bg/ $panel-default-text/$panel-inner-border/$panel-body-padding -> $card-bg/$card-cap-bg/$card-color/$card-border-color/ $card-spacer-y (added to mo/map_theme_to_bootstrap4.scss). - Navbar: BS4 only has navbar-light/navbar-dark TEXT color variables, no bg/border/hover-bg -- $navbar-default-*/$navbar-inverse-* call sites now read MO's $TOP_BAR_*/$LEFT_BAR_* theme variables directly instead of going through a bootstrap-sass-shaped intermediary. - Buttons: $btn-active-variants (_form_elements.scss) now derives from $theme-colors ($primary/$success/etc.) using color-yiq() for text contrast, rather than bootstrap-sass's hardcoded white text -- correct even where a variant's background is light (e.g. warning). - Alerts: BS4 computes alert colors dynamically (theme-color-level()), with no exposed per-variant variables -- inlined the same rgba(..., 0.2) soft-background formula against the BS4-native color name at each call site instead of naming new bridge variables. - $input-border -> $input-border-color, $input-bg-disabled -> $input-disabled-bg, $dropdown-border -> $dropdown-border-color, $list-group-border -> $list-group-border-color, $hr-border -> $hr-border-color, $border-radius-base -> $border-radius, $brand-danger -> $danger, $text-color/$text_color -> $body-color, $padding-base-vertical/-horizontal/$padding-small-vertical -> $input-btn-padding-y/-x/-y-sm. - $carousel-text-shadow (a design constant, not theme-derived) and the 480px top-nav sub-breakpoint (no BS4 tier at that width) inlined as literals instead of named variables. mo/map_theme_vars_to_bootstrap_vars.scss is deleted -- every variable it defined is now either unused or replaced at its call sites. mo/_variables.scss (an unwired, complete bootstrap-sass _variables.scss copy) was a useful reference throughout this pass for what each dropped name used to resolve to; left in place, still unimported. Issue #5348. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
border-radius/border-radius-sm/border-radius-lg, btn-border-radius, input-border-radius, and alert-border-radius were all 5px -- now 0.3125rem, matching how the rest of this file (font-size-base, btn-padding-y/x, navbar-padding-y/x) already expresses measurements. card-spacer-y no longer needs a value here: BS4's stock 0.75rem already equals bootstrap-sass's old 12px panel padding at a 16px root (MO doesn't override html's font-size). Only card-spacer-x needs narrowing from BS4's wider 1.25rem default, expressed in rem to match rather than as a 12px literal. Border widths (input-border-width, alert-border-width), the tooltip max-width, and the grid gutter stay in px -- that already matches BS4's stock values for those specific variables. Issue #5348. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
Every assertion pinned to Bootstrap 3's "in" class for an expanded collapse pane now checks Components::Collapsible::EXPANDED_CLASS (currently "show") instead of a hardcoded literal, across the component/view/system/controller tests exercising it: Collapsible, Accordion, the InatImports/aliases/reasons-fields/BlockedIps forms, the observation and search forms, and the create-observation spare-slip flow. One functional fix alongside the test updates: mo/_icons.scss's `tbody.collapse.in` rule (overriding Bootstrap's collapse display:block back to table-row-group for a collapsible <tbody>) would have silently stopped matching anything under BS4 -- fixed to `.show`. Issue #5348. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
names_lookup_field_group.rb#collapse_class returns a bare "in"/nil string, not a call through Components::Collapsible -- a hand-rolled duplicate of the same expanded-state logic. Every earlier sweep for this migration grepped for the CSS-selector shape (.in), which doesn't match a plain Ruby string return, so this survived until the search form's collapse-expanded tests caught it live. Issue #5348. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
`variant:` renames :default/:inverse to BS4's :light/:dark, matching Bootstrap's navbar-light/navbar-dark classes. RIGHT_CLASS/LEFT_CLASS switch from BS3's float-based navbar-right/navbar-left to BS4's margin-auto utilities (ml-auto/mr-auto). The `.navbar.navbar-flex` bridge rule in mo/_top_nav.scss is deleted: checked the installed bootstrap-4.6.2.1 gem's _navbar.scss and confirmed BS4's `.navbar` already sets the same display/flex-wrap/ align-items/justify-content the bridge rule copied, and the bridge's padding value is moot regardless since Components::Navbar#base_class always adds `.p-0 !important`. Updated mo/_layout.scss and mo/_top_nav.scss's remaining `.navbar-default` selectors to `.navbar-light` to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
Re-vendoring jquery via `bin/importmap pin jquery@3.7.1` during the gem swap replaced the working classic-UMD jquery.js (which attaches window.jQuery/window.$ unconditionally) with jspm.io's ESM-wrapped build. That build always invokes jQuery's UMD factory with noGlobal=true (since window.document exists in a browser), so the "undefined"===typeof n gate for attaching globals doesn't fire. This broke every Stimulus controller calling bare $(...) (search-type_controller, tooltip_controller, jquery-events-to-dom-events) with "window.$ is not a function", and broke bootstrap.js's jQueryDetection with "Cannot read properties of undefined (reading 'jquery')" -- bootstrap.js does `import*as e from"jquery"`, which needs jquery.js's ESM `export default` to resolve to something with a usable `.fn` object. Patched jquery.js to unconditionally attach window.jQuery/window.$ (instead of gating on the noGlobal flag), keeping the ESM export intact so both consumers work from the same file. Found live via claude-in-chrome on the dev server: the search-bar "more options" toggle rendered but did nothing, console showed both errors, and test_faceted_search_panel_open_close_stays_on_page failed on #search_nav not gaining the expanded class. That same system test also had one leftover `.in` (pre-EXPANDED_CLASS) selector from before Components::Collapsible::EXPANDED_CLASS existed -- fixed alongside since it was blocking verification of the jQuery fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
…MD one The prior commit staged an intermediate state -- the classic UMD jquery.js restored from main, before the jspm-based patch was applied. That version has no ESM export, so bootstrap.js's `import*as e from"jquery"` resolves to an empty module namespace and Bootstrap's jQueryDetection throws. This commit is the patched jspm.io ESM build (unconditional window.jQuery/window.$ attachment, valid `export default`), confirmed via claude-in-chrome: search-bar collapse, tooltip, and Bootstrap's JS all run with no console errors. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
This test predates Components::Collapsible::EXPANDED_CLASS and still asserted the Bootstrap 3 .in class directly, so it failed on the very first assertion regardless of any other behavior -- which is what surfaced the jQuery global-attach regression fixed in the previous two commits. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
BS4 drops the .panel component entirely (renamed to .card), and MO's SCSS only patched color/border on top of Bootstrap's base panel box styling -- which no longer exists under the bootstrap gem. Panel was rendering completely unstyled as a box (no border, background, or padding structure) until this conversion. Components::Panel now emits card/card-header/card-body/card-footer/ card-title instead of panel/panel-heading/panel-body/panel-footer/ panel-title, and drops the panel-default variant class entirely (BS4 cards have no default-variant modifier). panel-collapse-trigger and panel-heading-links stay as MO-owned hooks; panel-collapse renames to card-collapse for consistency with the rest of the sweep. Collapsible's `panel:` kwarg keeps its name (still means "inside a Panel/card") but now emits card-collapse. The `sizing:` prop / .panel-sizing wrapper is removed outright -- its comment already flagged it "Bootstrap 3 only, remove when migrating to Bootstrap >= 4": BS4's flex-based .row/.col-* grid stretches columns to equal height by default, so the fixed-height hack Matrix::Box relied on isn't needed anymore. Two standalone leftovers fixed alongside: namings/header.rb's standalone panel-title h4, and projects/admin/show.rb's panel_class: "panel-danger" (BS4 has no danger-panel equivalent -- switched to the border-danger utility class, generated from $theme-colors). Verified live via claude-in-chrome: matrix boxes, the search-form cards, and the observations index all render as expected with the new card structure. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
.align-middle already exists as a BS4-native utility class (vertical-align: middle on the element itself) -- MO's version in mo/_utilities.scss duplicated that base rule and additionally special-cased combining it with .table on the same <table> element to cascade vertical-align: middle onto every cell, overriding Bootstrap's th/td vertical-align defaults. Reusing Bootstrap's class name for a second, table-wide meaning was confusing, and now that BS4 provides `.align-middle` as a utility class, it's a collision. Checked Bootstrap 4.6.2's _tables.scss: the th/td vertical-align:top / thead th vertical-align:bottom defaults this override targets are unchanged from Bootstrap 3, so the rule still does work under the new gem. Split the SCSS into a standalone .table-align-middle.table rule and updated the only two callers that combine it with .table on the <table> element itself (Admin::BlockedIps::Manager, Admin::BlockedIps::Edit::IpSummary). Every other .align-middle caller (individual <td>s, Components::Table column classes) just wants Bootstrap's per-cell utility and is unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
app/assets/stylesheets/mo/_links_buttons_alerts.scss got a large badge-id rewrite from origin/main that still used bootstrap-sass's $text-color, which doesn't exist under the BS4 gem -- broke the Amanita and BlackOnWhite theme compiles. The sibling rule two lines above already uses $body-color for the same background-swap-on-hover effect, confirming that's the intended variable. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
…up log-details/footer markup Column-generated classes were still BS3 syntax (col-xs-N/col-xs-offset-N), which don't exist in BS4's compiled CSS -- fixed to col-N/offset-N. Removed Column entirely from the sidebar and #right_side (fixed-width CSS instead), restored top-bar/sidebar border colors BS4's navbar-light/dark classes don't set, fixed base font size, small/.small sizing, and vote-btn-group sizing to match the old BS3 ratios. Renamed Components::Matrix::Table/Box/Carousel to Components::Grid/ Grid::Box/Grid::Box::Carousel (including the .matrix-box CSS class to .grid-box, the "matrix_box" turbo-stream context string to "grid_box", the display_opts[:matrix] index option to :grid, and matrix_caches_in_this_request? to grid_caches_in_this_request?) -- Matrix::Table was a literal <table> in the pre-Phlex layout; the current implementation is a <ul> with BS4 row-cols-* utilities, so the name no longer fit. DOM ids, the context: "matrix_box" turbo-stream target strings tied to it, and Observation.matrix_box_includes were left unrenamed -- functionally coupled to turbo-stream target matching and out of scope for this pass. Reworked the grid box's details/footer markup: the small/.small wraps on every line (log-where, log-when-who, source-credit, occurrence-link, rss-detail, user-detail, updated-at) compounded with the wrap's own reduced font-size, rendering everything far smaller than intended -- replaced with a single .log-text size on the details/footer wrap and a ul.list-unstyled > li.hanging-indent structure for each line, reusing the existing .hanging-indent utility instead of introducing a competing one. Moved the title's IDBadge out of the <h5> into a sibling flex wrapper now that its size is set directly via the size: prop rather than inherited from the heading's font-size. Restored BS4's dropped muted-small-in-headings behavior for the author-citation text. Mixed card-header/footer's background color between the card and page background instead of a small darken of the card's own color (readable in both light and dark themes). Removed trailing periods from the rss-log detail translations (some had them, most didn't) and gave the comment log entries their own template with a line break before the summary, since comment text is often long. CACHE_VERSION bumped repeatedly across this work (now v10) -- each of the rendered-HTML changes above (class renames, markup restructure, badge move/resize, translation text) invalidates previously-cached Grid::Box fragments. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
…gative margins The data-nav-target="offcanvas" wrapper used Components::Row, which unconditionally adds .row -- BS4's .row sets display:flex plus negative left/right margins (half the gutter width), and those margins fought #main_container's px-sm-3 padding, pushing content past its edge. Rendered the wrapper as a plain div instead: it isn't a Bootstrap grid row -- .row-offcanvas/.row-offcanvas-left already provide the position/transition/slide mechanism -- and moved the display:flex + flex-wrap:wrap that keeps the sidebar and #right_side side by side onto .row-offcanvas directly, without the negative margins .row also carried. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
…tional .row class Components::Row always adds "row" (Bootstrap's negative-margin grid class) with no opt-out, which fought #main_container's .px-sm-3 padding on the sidebar-offcanvas wrapper. Switched that wrapper to a plain div and moved the needed flex layout directly onto .row-offcanvas instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
…e dark theme Carousel controls now match Bootstrap 4's carousel markup (https://getbootstrap.com/docs/4.6/components/carousel/): a <button type="button"> carrying data-target/data-slide as a sibling of .carousel-inner, named .carousel-control-prev/-next, instead of an <a role="button"> inside a .carousel-control-wrap div. Positioned per-button via absolute positioning against .carousel's position: relative, matching the gem's _carousel.scss. Icon size reduced to match Bootstrap 3's $carousel-control-font-size (1.25rem / 1.875rem at md+, was 2rem/3rem), and the controls' bottom offset now accounts for the indicator strip's height so the icon centers over the photo, not the photo+indicators combined box. .carousel-inner's background switched from .bg-light (Bootstrap's $LIGHT_COLOR, a distinct accent color per theme -- a pale silver in Amanita, clashing with its dark page background) to a new .bg-body utility tied to $body-bg, the theme-aware page background. Named to match Bootstrap 5.3's .bg-body so it is a no-op rename at a future upgrade. .card/.card-title/.card-header padding and margin brought in line with Bootstrap 3's .panel defaults, which Bootstrap 4's .card leaves to the caller: .card gets back its margin-bottom (.grid-box resets it to 0, putting the margin on its <li> instead, since .card also carries h-100 there); .card-title's margin-bottom is zeroed (was inheriting $card-spacer-y's spacing, meant for body content, not a header title); .card-header gets a smaller vertical padding than $card-spacer-y provides, shared uniformly by $card-spacer-y/-x now converged to Bootstrap 3's $panel-body-padding (15px, both axes) instead of Bootstrap 4's smaller defaults. Also: Grid::Box's custom-block layout (glossary-term image removal grid) now wraps its content in Panel for card styling it was missing; Grid::CACHE_VERSION bumped for the resulting fragment shape changes; a handful of duplicate Bootstrap-4-default SCSS variables deleted from mo/_variables.scss (carousel, unused). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
…lass Bootstrap 4 renamed the class that makes a .fade element visible from .in to .show (bootstrap/_transitions.scss: .fade:not(.show) sets opacity: 0). Components::Modal's auto-open modal/backdrop and Form::UploadGallery::Item's remove-image button both still emitted "fade in", which no longer matches any rule under the BS4 gem -- both rendered permanently invisible (opacity: 0). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
… comments .card/.card-header/.card-title move from mo/_content.scss into a new mo/_card.scss, imported ahead of mo/_grid_box (whose .grid-box .card override stays put -- it's grid-box-specific, not generic card styling). Also trimmed a handful of comments across the carousel/ card/variables work that compared current values against Bootstrap 3's -- code comments should state the current fact, not narrate history nobody reads. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
Two corrections were fighting each other and landing icons below the text baseline: .inline-icon-link's vertical-align: middle, and a separate top: 2px nudge on a:not(.btn):not(.inline-icon-link) > .mo-icon / .list-group-item > .mo-icon (and their > span > variants). Removed both -- the vertical-align: middle is dropped from .inline-icon-link, and the top: 2px rule is deleted outright. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
Bootstrap 4's collapse data-API click handler calls preventDefault() and stopPropagation() unconditionally (confirmed in the vendored bootstrap.js), which blocks Turbo's click-driven frame navigation on a trigger that carries both data-toggle="collapse" and data-turbo-frame. The "Shared with [iNat] [MCP]" badges carry both, so the accordion pane opened correctly but its turbo-frame didn't fetch -- confirmed live: aria-expanded flipped and .show got added to the pane, but the frame's src/innerHTML stayed empty and no network request fired. Fix: give the turbo-frame a src + loading="lazy" instead of relying on the click. Turbo fetches it once the pane becomes visible, independent of Bootstrap's click handling. Also: info_frame.rb's "On iNaturalist:"/"On MyCoPortal:" label was an h5, sized too large for a compact accordion pane -- dropped to h6. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
) The file's header comment has said "All duplicates should be deleted here if updating to 4" since #5340's prep pass -- that pass flagged and fixed the 4 classes whose value diverged from Bootstrap's (font-weight-bold, border-top, border-bottom, list-group-flush) and left everything else as ordinary duplicates for deletion once the BS4 gem was loaded. This is that step. Removed: every m-*/p-*/mx-*/my-*/mt-*/mb-*/ml-*/mr-*/px-*/py-*/pt-*/ pb-*/pl-*/pr-*/mx-auto spacing utility Bootstrap generates natively at the base tier, plus the xs-tier d-*/float-*/text-align/align-items/ justify-content/flex-*/position-*/align-*/rounded-* utilities and their sm/md/lg/xl responsive variants, wherever the value matched Bootstrap's. A handful matched in every respect except missing !important (.my-0, .flex-column/.flex-row/.flex-wrap, .rounded-circle) -- deleted too, since Bootstrap's !important version already wins any cascade fight the non-!important MO version was winning by luck. Three classes sat at a value that doesn't match Bootstrap's scale (.mt-5/.mb-5/.pb-5 at 2rem vs Bootstrap's 3rem) with live callers across ~25 call sites -- deleted anyway per direction, accepting Bootstrap's value. Kept: classes Bootstrap has no equivalent for (.min-w-100/.max-w-100/.min-h-100/.max-h-100, .indent, .bg-*, the .table-align-middle cascade helper, etc.), plus .mx-sm-2/ .mx-sm-3/.mr-sm-4 (em-based, not matching Bootstrap's rem scale, live callers in top_nav.rb -- flagged, not verified against which value the page needs). 937 -> 336 lines. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
Missed in the previous sweep -- matches Bootstrap's img-fluid() mixin (max-width: 100%; height: auto;). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
Byte-for-byte copy of Bootstrap's utilities/_stretched-link.scss, already commented "delete after upgrade" -- upgrade's here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
… _content.scss Page-specific styling, not utility classes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
Bootstrap's \$overflows list only generates auto/hidden in this gem version -- .overflow-visible stays (not a duplicate), .overflow-hidden matches and is removed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
#right_side's calc-based width reduction applied at every breakpoint, even though .sidebar-offcanvas is absolutely positioned and slid off-screen below lg -- so the main content column was losing 9.9rem + 24px of width on mobile for a sidebar that isn't there. Restructured mobile-first, matching MO's pre-BS4-cutover approach (bootstrap-sass era, min-width(lg)-gated): .sidebar-offcanvas's off-screen positioning and the .row-offcanvas-*/.active offset rules are now the unguarded base (mobile) rules; a single min-width(breakpoint-min(lg)) block resets .sidebar-offcanvas to position: static and applies #right_side's width reduction, once the sidebar has room to sit in-flow. Left `hidden-overflow-x`/ `navbar-light`/`navbar-form` in a separate max-width(md) block -- unrelated to this layout mechanism. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
All test-only fixes -- each assertion was pinned to markup/classes from before a component change earlier on this branch, not caught locally since these span multiple test types (component, controller, integration). - show_prev_next_nav_test.rb: dropped the mr-auto assertion -- that class was removed from BTN_CLASSES earlier on this branch. - external_links_test.rb (component) x4: .p-3 -> .p-card, matching the component's switch to the .p-card utility. - external_links_controller_test.rb: h5 -> h6, matching InfoFrame's current heading level. - footer_test.rb: div -> li, matching Grid::Box::Footer's current hanging-indent li structure. - lurker_integration_test.rb: .badge-md -> .badge-lg, matching Grid::Box's current IDBadge size. - observations_integration_test.rb: added normalize_ws: true -- ObjectTitle's title span now sits inside a wrapping div (for the owner-naming line), which introduces incidental whitespace between the id badge and title text in the raw HTML that rack_test's text extraction doesn't collapse without it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NuatY3dLBZM9dzYFLzymjT
Several tests were pinned to a specific tag (h5/h6, div/li) or a sizing class (.badge-md/-lg) that changed under the BS4 cutover but carried no behavioral meaning. Replace with contract-level selectors (structural attributes, existence checks) that survive future markup tweaks. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…mark labeling InputGroup::Addon still emitted BS3's .input-group-btn/.input-group-addon, which BS4 doesn't style as flex children -- rebuilt it as .input-group-append/-prepend wrapping .input-group-text, matching BS4's markup. Removed the dead BS3 flex shim in _links_buttons_alerts.scss that targeted the old class names. The sort dropdown and the pagination goto widget were misaligned by a few pixels: a stray default <ul> margin-bottom on the sort bar (neither .list-unstyled nor .flex-bar reset it) and an asymmetric $btn-padding-y override that didn't apply to $input-padding-y. Converted the sort bar's <ul> to a <div> (Components::Dropdown gained an `element:` prop so its wrapper isn't hardcoded to <li>) and removed the button-only padding override so buttons and inputs share the same height. ShowPrevNextNav now uses Components::ButtonGroup instead of a hand-rolled <ul>/<li> list, wrapped in a <nav aria-label="Results navigation"> landmark. Audited every <nav> in the top-nav/title-bar/ sidebar area: `.show_title_nav` and `.show_object_nav` were unlabeled <nav> landmarks wrapping non-navigation content (title+edit-icons, interest-icons) -- demoted both to plain <div>s. Added aria-label to #top_nav and #sidebar so screen readers can tell the multiple <nav> landmarks apart. interest_icons.rb converted from a <ul class="nav"> to a <div class="btn-toolbar" role="toolbar">, since it's a cluster of borderless icon buttons, not a navigation list. Also swapped PatternSearch and Identify::Form from the MO-owned .flex-bar bridge class to BS4's native .form-inline, and dropped .flex-bar from top_nav.rb's CONTAINER_CLASSES -- it's a .container-fluid inside .navbar, which BS4 already gives flex + justify-content: space-between + align-items: center. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…input-group/dropdown patterns The Actions/user dropdowns pushed #top_nav's height open instead of floating over the page: BS4's `.navbar-nav .dropdown-menu` defaults to `position: static` (meant for a collapsed/stacked navbar-nav), and #top_nav doesn't collapse via BS4's mechanism -- the offcanvas sidebar is the mobile nav. Added `.navbar-expand` (bare, unconditional) scoped to the right-side div, which supplies `position: absolute` without the two-row `w-100` stacking break `.navbar-expand`'s flex-flow change would cause on the outer nav. Both dropdown menus also get `.dropdown-menu-right` so they anchor to the trigger's right edge instead of overflowing off it. `.navbar-link` (`Components::Navbar::LINK_CLASS`/`LINK_CLASSES`) had zero CSS or JS consumers anywhere in the app -- removed the constants and every call site (ShowPrevNextNav, IndexPaginationNav's page links, PatternSearch's/SearchBar's toggle buttons), along with the raw-constant-value tests that only asserted the dead strings equaled themselves. `RIGHT_CLASSES`'s `justify-content-end` was inert: the `<ul>` it wraps already carries `ml-auto`, and an auto margin consumes all leftover main-axis space before `justify-content` gets to distribute any. Removed it. Moved the dropdown `<li>` spacing from a `_top_nav.scss` `@extend .ml-4` reaching into Phlex-rendered markup to an explicit `wrapper_class: "ml-2"` on each `Dropdown(...)` call. Unified the search-toggle and QR-code buttons under one shared `.btn-toolbar` instead of an asymmetric `.navbar-form` div + bare button. BS4 dropped BS3's `.has-feedback`/`.form-control-feedback` icon-overlay pattern entirely -- PatternSearch's and Identify::Form's search icons now use the documented replacement, an `.input-group` with the icon in a prepended `.input-group-text` (`Components::InputGroup` + `InputGroup::Addon`), verified against the identify form's live autocomplete dropdown to confirm its position: absolute suggestion list still anchors correctly. Deleted the fully-dead has-feedback/ has-search CSS block, including an already-orphaned `.page-input .form-control-feedback` rule with no live caller. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sorter's "Sort by:" label gets .pr-2 (breathing room before the dropdown when visible at sm+); the dropdown's wrapper_class drops px-2 for px-0 so it sits flush left against the sorter's pl-3 when the label is hidden on mobile. Also reduces the left-nav-toggle's padding and rebalances the QR-code button's margins at narrow widths. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…oup column gutter vertical-align: -0.15em on icon-bearing buttons replaces Bootstrap's default `middle`, which aligns to half the parent font's x-height above baseline rather than the visual center of surrounding text. $yiq-text-dark/$yiq-text-light now point at $DARK_COLOR/$LIGHT_COLOR instead of Bootstrap's stock $gray-900/$white -- $gray-900 is remapped to the theme's body text color, which is light in Amanita, so color-yiq() picked light text for buttons whose background should get dark text. List-group columns (namings table, comments) get a tighter 0.5rem gutter instead of Bootstrap's default; a column wrapping a nested .row is zeroed so its padding doesn't stack with the inner columns'. InlineCRUDLinks gets a margin_class (ml-1) via a new prop on InlineLinkBlock, distinct from that component's other four callers. Responsive body font size: 15px from the lg breakpoint up (was 14px everywhere), with .btn and .modal-body following since their rem-based sizing doesn't otherwise track body's computed size. Headings stay pinned to their existing rem values. Reorganizes app/assets/stylesheets/mo/_utilities.scss: several rules that were card/table-specific move to _card.scss/_tables.scss, and .dashed-border/.dotted-border consolidate into .border-dashed (form-images_controller.js updated to match). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Vote buttons and the selected-vote span now share matching padding and border-width (transparent border on the buttons, visible on the selected one), so casting a vote no longer changes the row height. Tooltip controller disposes a vote button's Bootstrap tooltip when its trigger element disconnects -- Turbo's turbo_stream.replace on the vote interface swaps the trigger out without firing Bootstrap's hide handlers, leaving the tooltip popup orphaned in the DOM. Drops the vote-buttons row's top margin and trims the carousel caption's .caption-content padding to reduce excess whitespace around the vote buttons. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ssertion hook .well/.well-sm had no BS4 equivalent and MO's .well rule supplied no box chrome -- three live callers (Help#render_well, Help::CollapseBlock, Form::CameraInfo) rendered an unstyled box. Components::Well replaces the hand-rolled divs with a bordered, slim-padded box (no background, no size variant). .help-block gains .form-text (BS4's display:block + top-margin class) for the plain field-help shape; the class stays as an identifier. Deletes the dead color: $card-color rules for .help-block/.help-note (both no-ops -- nothing sets a different color for either to override). Adds .claude/hooks/block_cosmetic_class_assertions.sh, blocking assert_html/assert_no_html/assert_selector/assert_includes/ assert_not_includes calls in test/**/*_test.rb that pin button chrome, margin/padding utilities (numeric, auto, or negative n<N> steps), or font-weight/text-muted classes -- a mechanical backstop after the same cosmetic-class-assertion anti-pattern recurred twice in one session despite being documented in project memory. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bootstrap 3's .checkbox/.radio reserved this gap via an absolutely-positioned input plus label padding-left; Bootstrap 4 doesn't define these classes, so nothing supplied the gap anymore -- confirmed live, checkbox input had margin-right: 0px. Adds margin-right: $custom-control-gutter (BS4's 0.5rem control-gutter variable) on the checkbox/radio input. .help-block.form-text gets margin-bottom: 0.5rem -- .form-text only sets margin-top, and the field wrapper above it has its bottom margin zeroed to avoid doubling the top-margin space, so nothing was adding space below the help text before the next element. Scoped to `.help-block.form-text` (not bare `.help-block`) so it doesn't collide with `.well`'s margin-bottom on the well-shaped variant. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cut narrative asides (a specific caller's margin choice compared against another class's own margin value, the "not an oversight" defensive framing) and redundant possessives, and dropped the margin_class default from the comment since that value is subject to change independent of the component's contract. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three related issues in Header::IndexPaginationNav's letter/page
"goto" widgets, found on the names index:
- "By letter" wrapped onto two lines. The label div had default
flex-shrink and no white-space protection, so it shrank and wrapped
under the flex row's space pressure once it moved inside the
input-group (see below) tightened that pressure further. Confirmed
in the browser: the fix is text-nowrap.
- The input-group-append button was 1px taller than the input
(35.4px vs 34px) in both widgets. `.btn` already has a `lg`+
breakpoint bumping its font-size to match body's bump (see the
existing rule in _elements.scss), but `.form-control` was missing
the same bump, so its line-height came out a pixel short at that
breakpoint. `align-items: stretch` can't fix this since
`.form-control` sets an explicit height, not `auto` -- confirmed by
toggling the button's font-size in the browser and watching both
heights converge. Added the same breakpoint rule for `.form-control`,
mirroring the existing `.btn` one.
- "By letter" was a plain div, not a <label> tied to the <input> it
describes. InputGroup::Addon gets a new `variant: :label` (a
<label for:> in the same input-group-text shape as `variant: :addon`,
with .font-weight-normal baked in -- the sitewide `label { font-weight:
bold }` rule would otherwise bold it). The label now lives inside the
input-group as a prepend, matching Bootstrap's input-group-with-label
convention, with an id unique per nav position (top/bottom) so the
label's for= doesn't collide when the nav renders twice on one page.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
# Conflicts: # test/components/grid_test.rb # test/system/observation_form_system_test.rb
Adds .px-card/.py-card, split out of the existing .p-card ($card-spacer-y $card-spacer-x), so horizontal-only or vertical-only card padding can be reused outside .card. #top_nav's two .container-fluid rows now use px-card instead of px-3, so top-nav content lines up with card content below it. Header::IndexPaginationNav's .pagination-top/.pagination-bottom bars carry the edge padding (px-card) directly now, instead of the sorter and pager children each carrying left-padding. Sorter's pl-3 is dropped accordingly. Also fixes a handful of stale exact-class-string test assertions that broke or were touched incidentally by this change (cosmetic utility classes and non-semantic div tag pins), per the ongoing bs4-cutover cosmetic-test-assertion cleanup. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApgAvYYM7QvhpaFeXb2CFV
content_padding's :no_panels branch and Components::ContentPadded both switch from p-3 to p-card, so plain (non-panel) page content lines up with card edges. Sets $dropdown-item-padding-y/-x (0.25rem/0.5rem) in map_theme_to_bootstrap4.scss as the single source of truth for dropdown-row padding, tighter than BS4's default (.25rem 1.5rem). #top_nav's form.button_to/button.btn-link dropdown rows and the autocompleter's virtual-list item rows reference these variables instead of a literal padding value. Nests li.dropdown-item under ul.virtual_list, scoping it to the autocompleter's list instead of every dropdown-item in the app. Moves the row's padding from li.dropdown-item to its descendant a. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApgAvYYM7QvhpaFeXb2CFV
Components::Dropdown, the autocompleter's virtual-list rows, and Views::Controllers::Observations::Identify::Form's dual-target dropdown all move from BS3-style <ul>/<li>-nested markup to BS4's flat convention: .dropdown-menu (a <div>) holds .dropdown-item elements (<a>/<form>) directly, no <li> wrapper. This is what lets the autocompleter's row padding live on the row itself instead of being split between a <li> and its nested <a> (patched earlier this session, now the structural fix that made those patches necessary in the first place). base_controller.js's row clone/scroll-math functions are updated to treat each row as the <a> directly. Browser-verified: DOM shape, keyboard/mouse row highlighting, click-to-select, and virtual-scroll past 10 matches all work unchanged after the markup change. Running the full system-test suite surfaced a pre-existing bug: Components::Accordion did not set data-parent on its collapse panes. Bootstrap 4's collapse.js reads data-parent off the collapse target that was clicked into (the pane), not the trigger that was clicked -- the codebase had it on the trigger instead, at four call sites, so sibling panes did not auto-close. Fixed at the source (Accordion's pane slot sets it now), and removed the dead trigger-side wiring (including the unused Form#cancel_parent prop) at every caller. Also fixes two stale BS3 collapse-class assertions (`.in`, replaced by BS4's `.show`) in system tests that predate this session's work. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApgAvYYM7QvhpaFeXb2CFV
AutocompleterField's collapsible help renders as a sibling right after .form-group -- when expanded, .form-group's default margin-bottom (~1rem) read as a gap between the field and the help describing it. .form-group now gets mb-2 in that case, and the outer .autocompleter wrapper gets mb-3 to keep the same overall spacing to whatever field comes next. Plain (non-collapsible) help was already handled correctly elsewhere and is untouched. Also fixes autocompleter_field's top-level helper: unlike text_field/textarea_field/checkbox_field/select_field, it did not call set_help_slot, so a plain `help: "..."` string with no block silently rendered no help (confirmed live against names/form.rb's correct_spelling field, which uses this shape). Needed to verify the margin fix in the browser in the first place. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApgAvYYM7QvhpaFeXb2CFV
Sidebar list-group-items no longer get a separate .indent class -- the heading styling (bold, disabled) already distinguishes headings from the links under them, so the extra left padding was redundant. Removed from Sidebar::CSS_CLASSES (both the standalone indent key and admin's " indent" suffix) and every consumer (Section, ContextNav, User, Languages). Swept the sidebar test suite for other cosmetic- class assertions (.font-weight-bold, .disabled, .btn.btn-link, .ml-2, non-semantic div tag pins) while in these files, deleting tests whose entire purpose was pinning styling. Also fixes .dropdown-menu not tracking body's responsive font-size bump (mo/_elements.scss bumps body to 0.9375rem at the lg breakpoint; Bootstrap's dropdown font-size is a compile-time rem value relative to the root, not to body, so it didn't follow). Consolidated into the existing shared breakpoint block alongside .btn/.form-control, which already had the same gap patched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApgAvYYM7QvhpaFeXb2CFV
Moves the collapsible search-bar row out of TopNav into a sibling component, Views::Layouts::SearchNav, rendered directly below TopNav (Views::Layouts::Application#render_right_side) instead of as a second row nested inside TopNav's navbar. SearchNav gets a Navbar wrap (independent padding, border-top-0 so it doesn't double up against TopNav's bottom border) instead of inheriting TopNav's. SearchBar moves out of the top_nav/ subdirectory to a flat Views::Layouts::SearchBar alongside it. The collapse target is the navbar wrapper itself, not a div nested inside it -- a collapsed inner div left the navbar's padding and border visible as an empty bar. Collapsing the whole navbar (`.collapse` class on the Navbar element itself) makes it disappear entirely. TopNav's `.mb-2` is dropped via `#top_nav:has(+ #search_nav.show)`/`.collapsing` while the search bar is open or mid-animation, since SearchNav's vertical padding already supplies that gap once it's visible -- otherwise the two stacked. Also fixes #pattern_search_form on mobile: removes extraneous .form-group wrapper divs around the type-select and submit button, adds flex-nowrap + min-width: 0 so the input group, select, and submit button share one line instead of wrapping, and gives the input group/select width: auto so they can shrink below their BS4-default width: 100%. Search Help / Advanced / Simple collapse toggles at either end of the search bar get pl-0/pr-0 so they sit flush against the container edge. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApgAvYYM7QvhpaFeXb2CFV
.faceted-search-form's flash target and form columns now sit inside a Row (flex-column, so its negative margin offsets the columns' padding on both axes instead of just horizontally), popping them out of the containing navbar's padding to align with the columns' edges rather than sitting inset from them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApgAvYYM7QvhpaFeXb2CFV
render_help_toggle, render_form_toggle (search_bar.rb), and render_search_bar_toggle (form/search.rb) pass size: :lg -- CollapseToggle has its size: prop already, no need to reach for Components::Button for that. Their padding drops the asymmetric pl-0/pr-2 vs pl-2/pr-0 edge scheme (BAR_TOGGLE_CLASSES removed) for a flat p-0 on all three; browser-tested, the surrounding elements still leave sufficient spacing. Also drops the .btn.btn-link cosmetic class assertions from search_bar_test.rb per the no-cosmetic-classes-in-component-tests convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApgAvYYM7QvhpaFeXb2CFV
Below md, the title bar's id badge, edit icons, and interest-icons/ pager now share one row (Header::PageTitle#render_mobile_top_row), with the object name/owner-naming on a separate row below. The id badge moves out of Header::ObjectTitle into its content_for(:id_badge) slot (Views::FullPageBase::Title#add_id_badge), so Header::PageTitle can place it twice -- next to the title on desktop, and again in the mobile row -- without an id collision (IDBadge, EditDeleteIcons, InterestIcons, and ShowPrevNextNav all emit no HTML id). Badge size moves to :xl since neither placement nests it inside a heading anymore. .h3.page-title and #owner_naming.h5 get mobile-first font-size rules (smaller by default, full size at sm+). Desktop's badge+title+edit-icons row switches from justify-content-between to justify-content-start with ml-auto on the edit-icons wrapper, so the badge sits close to the title instead of spread away from it, plus mr-3 mt-md-1 to align the badge with the title's baseline. Also: - Fixes #right_side overflowing horizontally on narrow viewports: a flex item's default min-width: auto blocked it from shrinking below an unconstrained descendant's content width (a full-bleed image, in the observation-show case). min-width: 0 on #right_side lets flex-shrink do its job. - Fixes String#small_author's inserted <small class="text-nowrap"> swallowing the leading space between the name and the author into the nowrap span, leaving the binomial's genus/epithet space as the only wrap point in the whole run -- narrow viewports broke the name itself instead of wrapping before the author. The space now stays outside the span, a normal wrap point. - interest-icons spacing: each eye button gets px-1, the toolbar gets ml-2. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApgAvYYM7QvhpaFeXb2CFV
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5348. WIP -- opened as a running record per that issue's sequencing, not ready for review. Won't be marked ready until every item in #5348 is verified live in the browser across themes.
Progress so far
gem("bootstrap-sass")forgem("bootstrap", "~> 4.6").bin/importmap pin bootstrap@4.6.2(an unpinnedbin/importmap pin bootstrapresolves to BS5, which drops jQuery -- would break every.collapse()/.modal()/.tooltip()jQuery plugin call in the app's Stimulus controllers).popper.jspinned alongside it since BS4's JS imports it directly.mo/map_theme_to_bootstrap4.scssinto the import chain ahead of@import "bootstrap", and fixed two bugs found while touching it:$gray-100/$gray-900were hardcoded to$LIGHT_COLOR/$DARK_COLORregardless of theme (backwards for a dark theme like Amanita) -- now derived from$BODY_BG_COLOR/$BODY_TEXT_COLOR, which already resolve light/dark per theme;$card-cap-bguseddarken()instead of the project'sscale-color()convention._utilities.scss'sfont-weight-bold/border-top/border-bottom/list-group-flushrules, duplicates of what BS4 already provides.Finding:
mo/map_theme_vars_to_bootstrap_vars.scssstays for nowThe plan was to replace this bootstrap-sass-named mapping file outright. It cannot be deleted yet: about 20 of MO's stylesheets read its variables directly (
$btn-default-color,$navbar-inverse-bg,$panel-default-text,$dropdown-link-hover-bg, and about 55 more names across_form_elements.scss,_left_nav.scss,_top_nav.scss,_name_lister.scss,_carousel.scss,_content.scss, and more), not just Bootstrap's component partials. Deleting it produces Sass "undefined variable" compile errors, not just visual drift. Both files are imported now: the new one for BS4-native names, the old one as a compatibility bridge for those ~20 files until each is swept to BS4-native names/mixins -- likely a separate pass within the HTML/CSS sweep step in #5348, given the size.Test plan
Components::Panelupdates, HTML sweep #5348 checklist items.article: no