AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Dependency-free accessible carousel component (esbuild)
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
Dependency-free scroll-snap slider/carousel. npm run size is the authority on every byte
figure and the only place one is written down: CI enforces a 6656 B gzip budget on the engine and
a 16 KiB guard on what a site actually downloads, and the demo masthead fetches the two shipped
files and gzips them in the browser, so its number cannot go stale either. Nothing else in this repo
repeats a current size — a figure copied into prose was 50% low within six weeks, because the same
two files grew the card styles and the shared pattern structure after it was written.查看完整 README ↗Dependency-free scroll-snap slider/carousel. npm run size is the authority on every byte
figure and the only place one is written down: CI enforces a 6656 B gzip budget on the engine and
a 16 KiB guard on what a site actually downloads, and the demo masthead fetches the two shipped
files and gzips them in the browser, so its number cannot go stale either. Nothing else in this repo
repeats a current size — a figure copied into prose was 50% low within six weeks, because the same
two files grew the card styles and the shared pattern structure after it was written. No build step
required to use, themed entirely with CSS custom properties. Built to be maintained in-house: the
whole engine is one commented file, src/dealeron-slider.js.
The browser owns the physics (touch, drag, momentum, snapping — CSS scroll-snap); the JS only
wires controls, state, autoplay, and the gallery variant — plus the one physics gap browsers leave
open: mouse drag-to-scroll (native scroll containers don't drag with a mouse;
data-dos-drag="false" opts out). Rewind instead of infinite loop: no cloned slides, so no
duplicate content for SEO and no screen-reader confusion.
The demo page (demo/index.html) is a workbench: pick a pattern, set it up (how many across at each
breakpoint, how many rows, brand preset, arrow colours, how many cards an arrow moves), and copy
code generated from those same settings — so the snippet is always exactly the slider on screen. It
also hands you all four dist/ files themselves, to upload — the engine is linked, never pasted. It
is live at https://stevenpelletier90.github.io/dealeron-slider/demo/ (the bare
/dealeron-slider/ redirects there), the link shared with DealerOn developers. GitHub Pages serves
master from the root with no build step, so a src/ change is live only once npm run build has
run and dist/ is committed and pushed. The repo keeps the name dealeron-slider on purpose: the
2026-08-31 rename left it alone so this link survives. Never rename the repo.
<link rel="stylesheet" href="dealeron-slider.min.css">
<script src="dealeron-slider.min.js" defer></script>
<div class="dos my-slider dos-sm-2 dos-md-3" data-dos aria-label="Featured vehicles">
<ul class="dos-track" role="list">
<li class="dos-slide">…</li>
<li class="dos-slide">…</li>
</ul>
</div>
Every [data-dos] element initializes automatically. Slides-per-view is CSS, not a JS option: the
dos-xs-N / dos-sm-N / dos-md-N / dos-lg-N classes ship in the stylesheet for N of 1–8, on
Bootstrap 3's tiers — 768 / 992 / 1200, the grid the storefronts run today — and since
2026-09-15 dos-576-N and dos-1400-N on Bootstrap 5's two extra tiers, 576 / 1400, named by
their width because Bootstrap 5's letters mean different numbers (its sm is 576; ours is 768 and
frozen). The three shared numbers mean the same thing in both grids, so one set of classes serves a
site before and after the platform moves. One class per tier where the count changes; the engine's
own default is one across, so dos-xs-1 is never needed.
Setting --dos-per-view by hand in a media query does the same thing and is what the classes are
made of, but on a DealerOn page your CSS goes in the Style Only field as raw CSS — no <style>
tags — so there is no in-page <style> block to put it in. Write .my-slider.dos { … } rather than
.my-slider { … } there: a bare class ties with the engine's own .dos, and then source order,
which you do not control, decides which wins.
import { DealerOnSlider } from './src/dealeron-slider.js';
const slider = new DealerOnSlider(document.querySelector('.my-slider'), { autoplay: 4000 });
JS options override data attributes, which override defaults.
class="dos" (+ data-dos for auto-init) with aria-label or aria-labelledby.my-slider — which is what your settings hang off.
It must be unique on the page. Two sliders sharing a name share their rules, and the second
block's CSS wins for both: measured on a pair of model bars, the first strip took the second's 3em
gap and its slides went from 208.6px to 180.6px, with nothing on the page saying so. The builder
puts the field beside its copy buttons for this reason..dos-track child — <ul role="list"> for card carousels, <div> for
data-dos-gallery. The role is not redundant here: see Accessibility behavior..dos-slide children. All real content (headings, links, images) goes in the HTML — the
engine never injects content, only controls.width/height; first visible image eager (add fetchpriority="high" only if the
slider is above the fold); later slides loading="lazy" decoding="async"; sizes = one slide's
rendered width.| Option | Data attribute | Default | Effect |
|---|---|---|---|
autoplay | data-dos-autoplay="4000" | 0 | Advance every N ms; adds pause button (first in tab order) |
rewind | data-dos-rewind="false" | true | Arrows wrap at the ends; false stops there and aria-disables the end arrow (ignored with autoplay, which needs the wrap) |
step | data-dos-step="slide" | "page" | "slide" advances one card per arrow click / autoplay tick instead of a full page; a positive integer (data-dos-step="3") advances that many. Dots still represent pages, and the last stop is always the end |
drag | data-dos-drag="false" | true | Mouse drag-to-scroll on the track (click-through suppressed after a real drag); touch/pen swiping is native and unaffected |
fade | data-dos-fade | false | Stacked crossfade instead of a scrolling track — 1-up heroes; no drag/peek, ignored with gallery |
gallery | data-dos-gallery | false | Tabbed thumbnail gallery (thumbs generated from slide images) |
roledescription | data-dos-roledescription | "carousel" | Empty string to omit |
labels | data-dos-label-* | English strings | Every announced string, one attribute each — see Announced text and other languages |
| — | data-dos-init="manual" | auto | Skip auto-init; construct via new DealerOnSlider(el, opts) from page script |
Every string the carousel announces is settable from the markup, because the CMS hands a designer markup and never a constructor call — a page built in the block editor has no other route to a Spanish carousel.
| Attribute | Default | Where it is heard |
|---|---|---|
data-dos-label-prev | Previous slides | Previous arrow |
data-dos-label-next | Next slides | Next arrow |
data-dos-label-pause | Stop automatic slide show | Pause button (autoplay only) |
data-dos-label-play | Start automatic slide show | Same button once paused |
data-dos-label-dots | Choose slide | The dot group |
data-dos-label-goto-slide | Go to slide {n} | One dot, 1-up |
data-dos-label-goto-page | Go to slides {from}–{to} | One dot, multi-card |
data-dos-label-status-single | Slide {n} of {total} | The live status region, 1-up |
data-dos-label-status-multi | Slides {from}–{to} of {total} | The live status region, multi-card |
data-dos-label-thumbs | Choose photo | The thumb strip (gallery) |
data-dos-label-photo | Photo {n} | One thumb (gallery) |
data-dos-label-slide-position | {n} of {total} | A slide's own name, when the track is not a list and the slide has no heading |
data-dos-label-slide | slide | The word for a slide on a track that is not a list, and for a gallery panel |
{n}, {from}, {to} and {total} are filled in; anything else is used verbatim. Set
data-dos-roledescription too — a screen reader says the word "carousel" before any of these.
<div
class="dos"
data-dos
lang="es"
aria-label="Modelos"
data-dos-roledescription="carrusel"
data-dos-label-prev="Anteriores"
data-dos-label-next="Siguientes"
data-dos-label-dots="Elegir diapositiva"
data-dos-label-goto-slide="Ir a la diapositiva {n}"
data-dos-label-status-single="Diapositiva {n} de {total}"
data-dos-label-slide="diapositiva"
></div>
The status region is also the shortest route to a visible "3 / 12" counter: it already tracks every move, so it only needs unhiding and wording.
.my-slider .dos-status {
position: static;
inline-size: auto;
block-size: auto;
margin: 0;
clip-path: none;
}
<div class="my-slider dos" data-dos data-dos-label-status-single="{n} / {total}" data-dos-label-status-multi="{from}–{to} / {total}"></div>
A JS labels option still wins over an attribute, so an existing manual-init page is unaffected.
The two files are meant to live at one URL every site links, and the filenames carry no version — so
a fix reaches every storefront at once, and so would a mistake. What makes that safe is the
contract, not the filename: class names (dos, dos-track, dos-slide, the generated control
classes), the data attributes, the --dos-* properties, the dos:* event payloads, the public
methods and the accessibility behaviours are frozen. Adding is fine. Renaming or repurposing is
not, because the sites already linking the file cannot be edited to keep up.
That rule was always written down. With a shared copy it stops being a preference and becomes the
only thing standing between a rename and every storefront that links it. The names have moved twice,
each time before any page was believed to use them: dl-carousel → cs on 2026-08-31, and cs →
dos on 2026-10-27, when the product became DealerOn Slider. They are frozen again from the first
upload of the dealeron-slider files.
Files named dl-carousel.* or custom-slider.* are not older versions of these. Each is a
different contract — dl-carousel had root class dl-carousel, --dlc-* properties and
window.DLCarousel; custom-slider had cs, --cs-* and window.CustomSlider. A page linking
either and pasting a snippet from the current builder gets an unstyled list, so they should be
removed rather than left alongside. See "Deployment status" below for what is on the shared path
today, and the rename map for moving a page across.
dist/dealeron-slider.min.css is the engine plus a small library of card styling: the seven
card looks and a set of column classes (npm run size prints the share). The engine itself styles
no cards on purpose — dos-* is mechanism, cargo-* is content — but every site that links it gets
the card half too, so a slider is mostly just its markup:
<link rel="stylesheet" href="/path/dealeron-slider.min.css" />
<script src="/path/dealeron-slider.min.js" defer></script>
<div class="dos cargo-tile dos-xs-2 dos-sm-3 dos-md-4 dos-lg-5" data-dos aria-label="Our models">
<ul class="dos-track" role="list">
…
</ul>
</div>
Looks: cargo-tile, cargo-vcard, cargo-wordmark, cargo-split, cargo-portrait, cargo-logo,
cargo-location. Columns: dos-xs-N / dos-sm-N / dos-md-N / dos-lg-N for N of 1–8, on
Bootstrap 3's tiers, plus dos-576-N / dos-1400-N on Bootstrap 5's two extra tiers. Anything you
change from a look's defaults goes in a short <style> block beside the markup — the builder writes
only the differences.
The card half is generated from the same look definitions the builder draws with
(scripts/build-cards.mjs appends it behind a /*! cards */ marker), so the file and the preview
cannot disagree. npm run size splits on that marker and weighs only the engine against the budget
below — the budget's job is to show the engine undercuts Embla's core and Splide, and neither of
those ships a card library.
There was a Paste the card styles too switch in the builder that inlined a look's rules into the
snippet, for a page linking a dealeron-slider.min.css older than the card half. It went on
2026-09-08, for the reason the paste-the-engine route went: an inlined copy can never be fixed, so
patching a card style reaches every linked page and silently misses every inlined one, with nothing
saying which pages are on which. The gap it covered closes with a re-upload — see "Deployment
status". It was also a no-op on most of the patterns, where it drew a control that announced it did
nothing.
Worth keeping in mind either way: the card CSS carries the card styling only, never the engine's
layout and physics, so on a page with no stylesheet linked at all a block renders as a full-width
vertical list with static arrows, whatever the column classes say. Measured, not assumed: track
display: block, overflow-x: visible, scroll-snap-type: none, slide flex-basis: auto.
Since 2026-09-14 both shared files carry a third section behind a /*! patterns */ marker
(scripts/build-patterns.mjs, run by npm run build): every pattern's structural CSS — the tab
row, the filter bar, the lightbox, the photo captions — and the six pattern scripts, each in its own
try/catch after the engine's own start-up. A pasted snippet is therefore values and markup and
nothing else: the Chevrolet tabbed bar went from 56 lines of CSS plus a script to 39 lines of
values, and a site pays for the structure once, cached, instead of on every page view — the platform
serves Style Only inline in every page.
The markup names its pattern on its outermost element, data-cargo="tabs", and every shared rule
hangs off that attribute at the lowest specificity that still beats the engine: :where() around
the attribute, so a root rule is (0,1,0) and a descendant rule one class more. The engine's own
rules tie and lose on source order, which is fixed because the section is appended after them in the
same file. A rule under your slider's own name is one class higher and wins in any order — that
is how a pattern is restyled on one site, one property at a time, and the only way. Copying the
shared rules into Style Only freezes them at that day's build.
Which means the shared files are the one place a pattern's structure is edited, and only whoever
uploads them edits it. The upload rule below widens accordingly: a change under src/ or to
demo/assets/patterns.js puts the hosted files a build behind.
A page gets the engine from the two <link>/<script> tags and no other way. There is no
paste-the-engine route any more, and adding one back needs a better reason than a page being
awkward.
Why, and it is not tidiness: a pasted engine can never be fixed. Patch a bug and every linked page picks it up on its next cache cycle. Every pasted copy silently does not — and nothing on those pages, in this repo, or in the folder says which sites are carrying which build. That is permanent, undetectable divergence across dealer sites nobody owns. Sharing one cached copy and not repeating 15 KB per page are real gains too, and both are secondary to that one.
docs/cms-no-hosting.md and scripts/paste.mjs used to be the escape hatch, written when nothing
was hosted yet. Both are deleted: the condition that justified them ended when the four files went
up (see Deployment status above), and the script emitted code onto live dealer pages with no test
over it — the only thing in this repo shipping to a dealer without a gate. Git has them if the case
ever turns out to be real, and it comes back with a test.
Note this is about the ENGINE. The per-slider CSS the copy panel hands over is still pasted, still
goes in Style Only, Head Section, and is still why .stylelintrc.json pins rgba() to the
legacy form.
Every one of them is tabulated with its live default on the Reference page, which reads them out of
the shipped stylesheet rather than counting by hand: --dos-per-view, --dos-gap, --dos-peek
(edge sliver of the next slide), --dos-arrow-size/fg/bg, --dos-arrow-fg-hover/bg-hover,
--dos-dot-size/fg/current, --dos-controls-space, --dos-thumb-w/h, --dos-thumb-hover-scale
(gallery thumb zoom on hover; 1 turns it off), --dos-focus, --dos-transition (duration+easing
for control colour transitions), --dos-fade-ms (crossfade duration in fade mode). Set them on the
.dos element or any wrapper.
Spacing defaults are em, never rem. rem is locked to <html>, and Bootstrap 3 — which the
storefronts run — sets html { font-size: 10px }, so every rem shipped at 62.5% of its intended
size on a real dealer page (the reserved dot row fell to 25px against a 24px dot hit box). em
tracks whatever the host sets on body, so the slider scales with the page it is pasted into. The
generated controls carry font: inherit for the same reason: a <button> otherwise takes 13.33px
Arial, and em inside it would mean something different from em outside it.
Methods: goTo(n), next(), prev(), pause(), play(), destroy(),
DealerOnSlider.autoInit(scope?) — the same name whether loaded as a classic script
(window.DealerOnSlider) or imported as an ES module. Instance is at element._dos. Events (bubble
from the root): dos:change {index, page, slidesInView}, dos:autoplay-start,
dos:autoplay-stop, dos:destroy.
goTo(n) takes a slide index: a finite whole number, clamped to the ends. A fraction truncates
(goTo(1.5) is slide 1) and anything that is not a number — NaN, Infinity, a word, undefined
— is a no-op rather than a throw. JS options beat data attributes for the CSS state too:
constructing with { gallery: false } or { fade: false } over authored data-dos-gallery /
data-dos-fade writes ="false" onto the element, so the stylesheet stops reserving the thumb
strip or pinning the slides one-up. destroy() puts the authored value back.
destroy() puts the root's attributes back to what they were before init and restores the authored
markup from a snapshot taken at init. The snapshot is a rebuild, not a mutation: the slides come
back as new nodes, so listeners or state a page script attached to the original slide elements do
not survive it. Re-attach after dos:destroy if you need them.
DealerOnSlider.wirePatterns() is set by the pattern section of dealeron-slider.min.js (not by
the engine, so it is absent from the ES module): it runs every pattern script again over the whole
document. It runs once by itself on load; call it only for pattern markup added afterwards, once per
batch — a tab row wired twice answers a key twice.
inert/aria-hidden on
off-screen cards (hiding corrupts announced counts).tabindex="-1" on it. Firefox otherwise gives every scroll container a stop, and NVDA read a
whole model bar aloud there before the next Tab reached the first card. Arrow keys still scroll
the strip from a focused card. Slides with nothing focusable (a review, a quote) get
tabindex="0" instead, so the strip stays reachable by keyboard.<ul>/<ol> track carries role="list" in the markup, and gets it re-applied at init for
markup that does not. The library's own list-style: none makes WebKit drop list semantics, which
would silently kill the "N of 6" count announcements in Safari/VoiceOver — don't remove either. A
validator calls the attribute redundant; on a styled list it is not. In the markup it covers the
moment before the script runs, and a page where the script never loads.aria-current="true" and stays an enabled, focusable button. Not aria-disabled, which it was
until 2026-10-27: NVDA reads that as "button, unavailable", which sounds broken, where
aria-current reads "button, current". Style the current dot with .dos-dot--current.data-dos-fits —
controls that cannot move anything must not be focusable, and a one-of-one dot group announces a
choice that isn't one. It is re-evaluated on resize, because slides-per-view is CSS. Style on
data-dos-fits if you want the reserved control space to collapse too.tablist with roving tabindex and arrow keys;
non-visible panels are inert. The visible panel takes tabindex="0": it holds no focusable
content, and without it Chrome puts an unnamed tab stop on the scrolling track instead (it does
that for any scroll container with no focusable children). Gallery tab order is prev → next →
panel → selected thumb. Each panel is a tabpanel with aria-roledescription="slide", as in the
W3C tabbed-carousel example: NVDA calls a bare tabpanel a "property page".prefers-reduced-motion, and turning
that setting on mid-session stops a rotation already running; status announcements are off while
rotating.data-dos-init="manual" and construct from page script:
new DealerOnSlider(el, { autoplay: 6000, labels: { next: 'Next vehicles' } }).dos:change on the root (bubbles) — e.g. update a counter,
lazy-init a map, sync anything to the current slide.a.addEventListener('dos:change', e => b._dos.goTo(e.detail.index)) — goTo is idempotent, so
feedback loops settle naturally.--dos-* custom properties per site/brand — no engine edits.The HTML on the sites is the stable API; this engine is an implementation detail. Any future engine (third-party or rewrite) must honor the same contract, and then replacing it = replacing the contents of the two dist files, with zero site edits:
.dos[data-dos] > .dos-track > .dos-slide+ with all content authored in the HTML;
generate its own controls (never require control markup in the CMS).data-dos-autoplay, data-dos-rewind, data-dos-step,
data-dos-drag, data-dos-fade, data-dos-gallery, data-dos-roledescription,
data-dos-init) and the --dos-* theming knobs.dos:* events with the same payloads and expose goTo/next/prev/pause/play/destroy +
DealerOnSlider.autoInit.data-dos-fits on the
root when every slide already fits (and removes it again on destroy()), data-dos-gallery /
data-dos-fade-on on the root to let the CSS reserve space, and data-dos-draggable /
data-dos-dragging on the track for the grab cursor. Its generated controls are .dos-controls,
.dos-arrow + .dos-arrow--prev / --next, .dos-pause, .dos-dots, .dos-dot +
.dos-dot--current, .dos-status.dos-sr-only, .dos-thumbs, .dos-thumb.dos-slide--current, on the visible
slide in fade mode. It was a bare is-current until 2026-09-08 — renamed in the last window
before the contract froze, because a name with no dos- stem can collide with whatever the
site's own theme already calls things, and the collision shows as a slide stuck visible or
invisible on their page with nothing to explain it. A replacement engine must use the same name;
nothing else it writes may land outside its own generated elements.Everything in 5 and 6 was undocumented until 2026-09-08. Undocumented does not mean unfrozen — a site can already be relying on it.
docs/cms-implementation.md — where the files go, the markup contract, replacement codes, per-OEM theming, and the ladder for each brand's model bar.
Since the 2026-10-27 rename to DealerOn Slider, the folder for these files is
/assets/shared/CustomHTMLFiles/Responsive/Apps/dealeronSlider/
⚠ and nothing has been uploaded to it yet. Until the four dealeron-slider.* files from dist/
are there, a page pasting a snippet from the current builder links two files that do not exist and
gets an unstyled list.
The previous folder, …/Apps/customSlider/, holds the four custom-slider.* files uploaded on
2026-09-08 — the cs contract, and a build behind even on its own names. Measured that day,
cache-busted against www.karlchevrolet.com: all four answered 200, last-modified 2026-09-08
14:01 GMT, three of them byte-identical to dist/ as it then stood, and the dl-carousel build
they replaced gone. Leave that folder until the pages linking it are known to be off it (see "Moving
a page off an older contract" below), then delete it.
After the first upload, keep it current by rule, not by number: if
git log src/ demo/assets/patterns.js has anything after the upload date written here, re-upload,
then update that date. Deliberately no byte figures: any commit touching src/ puts the folder out
of date, and a byte count written here goes stale the same day.
Do not expect dealeron-slider.min.css to match dist/ byte for byte even when it is current —
the platform re-minifies CSS it is given: :after becomes ::after, .5s becomes 500ms, .5
becomes 0.5, rgba(0,0,0,.8) becomes rgba(0,0,0,0.8), background: none becomes
background: 0, and --dos-peek: 0px becomes --dos-peek: 0. Rendered side by side on a
hostile host page at 1170px and four across, the two sheets resolved identically — slide 282px, flex
basis calc(25% - 10.5px), track padding 0px, scroll padding 0px, root padding-bottom 35px,
snap type, overflow and dot colour all equal. Only the token differs. Compare the other three for a
byte check, or prove the render; a byte difference in this one file is expected.
That unitless zero is worth reading twice, because it is F003 arriving from the platform rather
than from the builder. It is harmless here — --dos-peek is only ever consumed by padding and
scroll-padding-inline (src/dealeron-slider.css:80, :90), and both accept a unitless 0. It
would not be harmless on --dos-gap, which src/dealeron-slider.css:119 feeds to
calc((100% - (var(--dos-per-view) - 1) * var(--dos-gap)) / var(--dos-per-view)) — subtracting a
unitless number from a percentage is invalid, the declaration is dropped and the cards collapse to
content width. So okValue() refusing a bare 0 is no longer an inference about what the minifier
might do; the minifier has now been observed doing it.
npm run build writes four files to dist/, each in three sections — the engine, then the card
styles behind /*! cards */ (CSS only), then the pattern structure and scripts behind
/*! patterns */: dealeron-slider.css and dealeron-slider.js are the readable engine, for
anyone opening the file to see what it does; dealeron-slider.min.css and dealeron-slider.min.js
are the same code minified, and the .min pair is what every page links — the demo, the copy
panel's tags and the snippet at the top of this file all name it. The readable pair goes up beside
it so the folder holds both; no page should link it. The other two documents defer to this section;
do not restate a status in them.
Uploading (do not skip the cache step). The files are served with
cache-control: max-age=1814400 — 21 days — from behind Fastly. Overwriting a file does not shorten
that: a browser that already fetched the old one keeps it for up to three weeks.
npm run build, then npm run size, and confirm the gate is green.dist/ into the folder above, overwriting what is there. The demo's
install panel has a Download for each of the four and a Download all four button, under
"First, get the four files up there" — the same bytes as dist/, saved under their own names, if
that is easier to reach than the repo.curl -s ".../dealeron-slider.min.css?cb=$RANDOM" | head -c 40 should begin
.dos{--dos-per-view. Check last-modified too — a 200 alone only says something is there, not
that yours is.dealeron-slider.min.css to match dist/ byte for byte; the platform re-minifies
it (see above). The other three do match, so compare those if you want a byte check. What matters
for the .min.css is that it still renders the same — the paste-parity test in the verification
checklist is the way to prove that.The contract has had two earlier sets of names, and each is different throughout, so a page cannot be half-migrated. Rename map (— means the older engine had no such name on record):
dl-carousel (to 2026-08-31) | cs (2026-08-31 to 2026-10-27) | Current (dos) |
|---|---|---|
dl-carousel | cs | dos |
dl-carousel-track | cs-track | dos-track |
dl-carousel-slide | cs-slide | dos-slide |
| — | cs-* (controls, columns) | dos-* |
--dlc-* | --cs-* | --dos-* |
data-slider | data-cs | data-dos |
data-slider-* | data-cs-* | data-dos-* |
| — | cs:* events | dos:* events |
| — | element._cs | element._dos |
window.DLCarousel | window.CustomSlider | window.DealerOnSlider |
dl-carousel.{css,js} | custom-slider.{css,js,min.*} | dealeron-slider.{css,js,min.*} |
The card classes, cargo-* and data-cargo, did not change in the 2026-10-27 rename.
Finding the pages that link the shared folders is Steven's, and nothing is believed to be live on
either yet — the known consumer is a designer test page built on the dl-carousel classes. Migrate
a page by rebuilding its slider in the builder and re-pasting all three parts, rather than by
editing class names in place: the snippet's CSS, markup and script have to agree with each other.
The builder cannot read a snippet back in — there is no import, and settings reset on reload — so there are two supported routes, and which one depends on what changed:
<li class="dos-slide">…</li> and change it. Nothing else has to
move.A slider pasted before 2026-09-14 carries a script in Body Section, Bottom. Since then a
pattern's script ships inside dealeron-slider.min.js and its structure inside
dealeron-slider.min.css, so when the shared files are re-uploaded that pasted script must come
out, or the tabs are wired twice and a keyboard press moves two tabs. The pasted structure rules can
stay (they say what the shared file says) or go; the values must stay.
Either way the class name must not change. .my-slider is what the Style Only rules hook onto;
rename it and every setting silently stops applying. Give a second slider on the same page a
different name instead.
Replacement codes (#NAME#, #CITY#, #STATE#, #CONTACTUS#) resolve inside a Custom HTML block,
so they are safe in slide text and headings. They do not resolve in the Style Only field, and
#MISCPATH# in an img src only resolves once that dealer has the file in their gallery.
npm install
npm run build # src → dist (esbuild)
npm run size # build + gzip budget gate (fails at or over 6656 B for the engine) — and the
# authority on every byte figure; nothing else here repeats one
npm run serve # http://127.0.0.1:8137 (for Lighthouse)
src/ is the canonical, readable code; dist/ is the checked-in CMS build. Rebuild and re-commit
dist/ whenever src/ changes.
npm run size and npm run validate pass, and npm test is green — the browser checks under
@playwright/test (npx playwright test --list prints the current count; about 90 seconds).
They cover what a linter cannot: that the pasted code still lays itself out, and lays itself out
the way the preview did. Steps 3, 4, 6 and 7 below now run in there too
(tests/engine.test.mjs), so what is left in this list is the part a machine cannot answer.npm test. Still by hand: gallery tabs respond
to Arrow/Home/End, and focus is never trapped or lost.npm test. Still by hand: stops on drag.npm test on authored markup.data-dos-fits) and
controls that cannot move anything are not focusable — asserted by npm test. Worth an eye
at a real window anyway.data-dos-rewind="false".
gallery + autoplay together is unsupported (autoplay is ignored, console warning).--strip-bg
paints the strip, not the page width, for the same reason.inert — the same
carve-out gallery mode has, and the reason the multi-card "never inert off-screen cards" rule
does not apply here (fade is 1-up, so no count is corrupted). A slide containing focus is never
inerted. Fade never scrolls, so goTo() is its commit point instead of scrollend.data-dos-fade-on, which the engine sets at init — never to
the authored data-dos-fade. JS decides which slide is visible, so if the stacking applied
without JS every slide would sit at opacity 0 and the whole carousel would vanish. With JS off the
track stays an ordinary scrollable strip with all slides visible. destroy() removes the marker.
The width is the one exception: --dos-per-view: 1 is pinned in the stylesheet off the
authored data-dos-fade, so a fading carousel is one across from first paint and init shifts
nothing. Without it, a hero authored three across laid out as a three-across strip and jumped a
whole image height when the script ran — measured 195.1px to 529.4px on a 1170px page — and that
is also what a no-JS visitor was left looking at. It means the dos-*-N classes and your own
--dos-per-view are ignored on a fading carousel, which is intended: a crossfade is 1-up.
data-dos-fade="false" keeps its columns.prefers-reduced-motion at call time.
Never add CSS scroll-behavior: smooth — but auto IS declared on the track and must stay:
without it a host page's global * { scroll-behavior: smooth } captures every instant scroll the
engine makes, reduced motion included.