22 CSS Countdown Timers13 / 22
Subscription Trial Expiry Countdown Banner
An in-app trial banner that escalates as the window closes: a calm purple state with seven days left, a magenta critical state inside the last two days, and a depleting bar in both. Dismissal is a checkbox and a :has() rule, so it costs no script — and this is an authenticated surface, not a site-wide promo bar.
Published
The code
<section class="cdt-13" aria-labelledby="cdt-13-title">
<input class="cdt-13__tgl" type="checkbox" id="cdt-13-theme">
<label class="cdt-13__tglbtn" for="cdt-13-theme">
<svg class="cdt-13__moon" viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><path d="M15.7 12.8A6.5 6.5 0 0 1 7.2 4.3a6.6 6.6 0 1 0 8.5 8.5Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>
<svg class="cdt-13__sun" viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><circle cx="10" cy="10" r="3.7" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 1.8v2.1M10 16.1v2.1M1.8 10h2.1M16.1 10h2.1M4.2 4.2l1.5 1.5M14.3 14.3l1.5 1.5M15.8 4.2l-1.5 1.5M5.7 14.3l-1.5 1.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
<span class="cdt-13__darktext">Dark mode</span><span class="cdt-13__lighttext">Light mode</span>
</label>
<div class="cdt-13__stage">
<h2 class="cdt-13__title" id="cdt-13-title">Trial banner · two escalation states</h2>
<div class="cdt-13__app">
<div class="cdt-13__chrome" aria-hidden="true"><span>Quill</span><em>Nova workspace · Alex Chen</em></div>
<div class="cdt-13__wrap">
<input class="cdt-13__x" type="checkbox" id="cdt-13-d1">
<div class="cdt-13__banner" data-state="calm" style="--left: 58%" role="timer">
<span class="cdt-13__ico" aria-hidden="true">
<svg viewBox="0 0 20 20" width="17" height="17"><circle cx="10" cy="10" r="7.6" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 6v4.3l2.8 1.7" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
</span>
<p class="cdt-13__copy"><strong><time datetime="P7D">7 days left</time> in your trial.</strong> Keep unlimited documents and version history by adding a plan.</p>
<div class="cdt-13__act"><button class="cdt-13__btn" type="button">Choose a plan</button></div>
<label class="cdt-13__close" for="cdt-13-d1" title="Dismiss for this session"><span class="cdt-13__srlabel">Dismiss trial banner for this session</span><svg viewBox="0 0 14 14" width="13" height="13" aria-hidden="true"><path d="M2.5 2.5l9 9M11.5 2.5l-9 9" stroke="currentColor" stroke-width="1.6"/></svg></label>
<div class="cdt-13__bar" aria-hidden="true"><i></i></div>
</div>
</div>
<div class="cdt-13__wrap">
<input class="cdt-13__x" type="checkbox" id="cdt-13-d2">
<div class="cdt-13__banner" data-state="critical" style="--left: 9%" role="timer">
<span class="cdt-13__ico" aria-hidden="true">
<svg viewBox="0 0 20 20" width="17" height="17"><path d="M10 2.4 1.6 16.6h16.8L10 2.4Z" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 7.4v4.1M10 13.4v1" stroke="currentColor" stroke-width="1.5"/></svg>
</span>
<p class="cdt-13__copy"><strong><time datetime="PT41H">41 hours left</time> — then editing locks.</strong> Your documents stay safe and read-only. No card on file yet.</p>
<div class="cdt-13__act"><button class="cdt-13__btn" type="button">Add payment method</button></div>
<label class="cdt-13__close" for="cdt-13-d2" title="Dismiss for this session"><span class="cdt-13__srlabel">Dismiss trial banner for this session</span><svg viewBox="0 0 14 14" width="13" height="13" aria-hidden="true"><path d="M2.5 2.5l9 9M11.5 2.5l-9 9" stroke="currentColor" stroke-width="1.6"/></svg></label>
<div class="cdt-13__bar" aria-hidden="true"><i></i></div>
</div>
</div>
<div class="cdt-13__doc" aria-hidden="true">
<span class="cdt-13__h"></span><span></span><span></span><span class="cdt-13__short"></span>
</div>
</div>
<p class="cdt-13__note">An authenticated in-app surface scoped to one workspace, not a site-wide promo bar. Dismissal is a checkbox plus a <code>:has()</code> rule, so it lasts for the session and costs no script.</p>
</div>
</section><section class="cdt-13" aria-labelledby="cdt-13-title">
<input class="cdt-13__tgl" type="checkbox" id="cdt-13-theme">
<label class="cdt-13__tglbtn" for="cdt-13-theme">
<svg class="cdt-13__moon" viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><path d="M15.7 12.8A6.5 6.5 0 0 1 7.2 4.3a6.6 6.6 0 1 0 8.5 8.5Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>
<svg class="cdt-13__sun" viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><circle cx="10" cy="10" r="3.7" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 1.8v2.1M10 16.1v2.1M1.8 10h2.1M16.1 10h2.1M4.2 4.2l1.5 1.5M14.3 14.3l1.5 1.5M15.8 4.2l-1.5 1.5M5.7 14.3l-1.5 1.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
<span class="cdt-13__darktext">Dark mode</span><span class="cdt-13__lighttext">Light mode</span>
</label>
<div class="cdt-13__stage">
<h2 class="cdt-13__title" id="cdt-13-title">Trial banner · two escalation states</h2>
<div class="cdt-13__app">
<div class="cdt-13__chrome" aria-hidden="true"><span>Quill</span><em>Nova workspace · Alex Chen</em></div>
<div class="cdt-13__wrap">
<input class="cdt-13__x" type="checkbox" id="cdt-13-d1">
<div class="cdt-13__banner" data-state="calm" style="--left: 58%" role="timer">
<span class="cdt-13__ico" aria-hidden="true">
<svg viewBox="0 0 20 20" width="17" height="17"><circle cx="10" cy="10" r="7.6" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 6v4.3l2.8 1.7" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
</span>
<p class="cdt-13__copy"><strong><time datetime="P7D">7 days left</time> in your trial.</strong> Keep unlimited documents and version history by adding a plan.</p>
<div class="cdt-13__act"><button class="cdt-13__btn" type="button">Choose a plan</button></div>
<label class="cdt-13__close" for="cdt-13-d1" title="Dismiss for this session"><span class="cdt-13__srlabel">Dismiss trial banner for this session</span><svg viewBox="0 0 14 14" width="13" height="13" aria-hidden="true"><path d="M2.5 2.5l9 9M11.5 2.5l-9 9" stroke="currentColor" stroke-width="1.6"/></svg></label>
<div class="cdt-13__bar" aria-hidden="true"><i></i></div>
</div>
</div>
<div class="cdt-13__wrap">
<input class="cdt-13__x" type="checkbox" id="cdt-13-d2">
<div class="cdt-13__banner" data-state="critical" style="--left: 9%" role="timer">
<span class="cdt-13__ico" aria-hidden="true">
<svg viewBox="0 0 20 20" width="17" height="17"><path d="M10 2.4 1.6 16.6h16.8L10 2.4Z" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 7.4v4.1M10 13.4v1" stroke="currentColor" stroke-width="1.5"/></svg>
</span>
<p class="cdt-13__copy"><strong><time datetime="PT41H">41 hours left</time> — then editing locks.</strong> Your documents stay safe and read-only. No card on file yet.</p>
<div class="cdt-13__act"><button class="cdt-13__btn" type="button">Add payment method</button></div>
<label class="cdt-13__close" for="cdt-13-d2" title="Dismiss for this session"><span class="cdt-13__srlabel">Dismiss trial banner for this session</span><svg viewBox="0 0 14 14" width="13" height="13" aria-hidden="true"><path d="M2.5 2.5l9 9M11.5 2.5l-9 9" stroke="currentColor" stroke-width="1.6"/></svg></label>
<div class="cdt-13__bar" aria-hidden="true"><i></i></div>
</div>
</div>
<div class="cdt-13__doc" aria-hidden="true">
<span class="cdt-13__h"></span><span></span><span></span><span class="cdt-13__short"></span>
</div>
</div>
<p class="cdt-13__note">An authenticated in-app surface scoped to one workspace, not a site-wide promo bar. Dismissal is a checkbox plus a <code>:has()</code> rule, so it lasts for the session and costs no script.</p>
</div>
</section>.cdt-13 {
--page: #f7f5fd;
--app: #ffffff;
--ink: #191430;
--muted: #675e8c;
--rule: #e6e1f5;
--accent: #6d3ce0;
--crit: #d61f7a;
--font-display: "Space Grotesk", "Geist", ui-sans-serif, system-ui, sans-serif;
width: 100%;
min-height: 100vh;
display: block;
box-sizing: border-box;
background: var(--page);
color: var(--ink);
font-family: var(--font-display);
}
@supports (color: oklch(50% 0.1 20)) {
.cdt-13 {
--page: oklch(97% 0.014 300);
--app: oklch(100% 0 0);
--ink: oklch(23% 0.06 300);
--muted: oklch(50% 0.06 300);
--rule: oklch(92% 0.02 300);
--accent: oklch(52% 0.22 295);
--crit: oklch(56% 0.24 350);
}
}
.cdt-13 *,
.cdt-13 *::before,
.cdt-13 *::after {
box-sizing: border-box;
}
.cdt-13__stage {
display: grid;
gap: 18px;
align-content: center;
min-height: 100vh;
max-inline-size: 880px;
margin-inline: auto;
padding: clamp(16px, 4vw, 48px);
}
.cdt-13__title {
margin: 0;
font: 600 clamp(18px, 3vw, 24px)/1.2 var(--font-display);
letter-spacing: -0.02em;
}
.cdt-13__app {
padding: 12px;
background: var(--app);
border: 1px solid var(--rule);
border-radius: 16px;
box-shadow: 0 20px 46px -30px rgba(109, 60, 224, 0.5);
}
.cdt-13__chrome {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 4px 10px;
padding: 6px 6px 14px;
border-block-end: 1px solid var(--rule);
}
.cdt-13__chrome span {
font: 700 14px/1 var(--font-display);
}
.cdt-13__chrome em {
font: 500 11.5px/1 var(--font-display);
font-style: normal;
color: var(--muted);
}
.cdt-13__wrap {
margin-block-start: 12px;
}
.cdt-13__x {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.cdt-13__wrap:has(.cdt-13__x:checked) .cdt-13__banner {
display: none;
}
.cdt-13__x:focus-visible + .cdt-13__banner .cdt-13__close {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.cdt-13__banner {
position: relative;
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 6px 10px;
padding: 12px 40px 14px 12px;
border-radius: 12px;
overflow: hidden;
background: rgba(109, 60, 224, 0.08);
border: 1px solid rgba(109, 60, 224, 0.24);
color: var(--accent);
}
@supports (color: color-mix(in oklab, red, blue)) {
.cdt-13__banner {
background: color-mix(in oklab, var(--accent) 9%, var(--app));
border-color: color-mix(in oklab, var(--accent) 28%, var(--app));
}
}
.cdt-13__banner[data-state="critical"] {
background: rgba(214, 31, 122, 0.1);
border-color: rgba(214, 31, 122, 0.3);
color: var(--crit);
}
@supports (color: color-mix(in oklab, red, blue)) {
.cdt-13__banner[data-state="critical"] {
background: color-mix(in oklab, var(--crit) 11%, var(--app));
border-color: color-mix(in oklab, var(--crit) 32%, var(--app));
}
}
.cdt-13__ico {
display: flex;
padding-block-start: 1px;
}
.cdt-13__copy {
grid-column: 2;
margin: 0;
min-inline-size: 0;
font-size: 13px;
line-height: 1.5;
color: var(--muted);
text-wrap: pretty;
}
.cdt-13__copy strong {
color: inherit;
font-weight: 600;
}
.cdt-13__banner[data-state="critical"] .cdt-13__copy strong {
font-weight: 700;
}
.cdt-13__copy time {
font-variant-numeric: tabular-nums;
}
.cdt-13__act {
grid-column: 2;
}
.cdt-13__btn {
min-block-size: 44px;
min-inline-size: 44px;
padding: 11px 16px;
border: 0;
border-radius: 9px;
background: var(--accent);
color: #fff;
font: 600 13px/1 var(--font-display);
cursor: pointer;
}
.cdt-13__banner[data-state="critical"] .cdt-13__btn {
background: var(--crit);
}
.cdt-13__btn:hover {
filter: brightness(1.08);
}
.cdt-13__btn:focus-visible {
outline: 2px solid var(--ink);
outline-offset: 2px;
}
.cdt-13__close {
position: absolute;
inset-block-start: 8px;
inset-inline-end: 8px;
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border-radius: 9px;
color: inherit;
cursor: pointer;
}
.cdt-13__close:hover {
background: rgba(0, 0, 0, 0.06);
}
.cdt-13__srlabel {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.cdt-13__bar {
position: absolute;
inset-block-end: 0;
inset-inline: 0;
block-size: 3px;
background: rgba(0, 0, 0, 0.08);
}
.cdt-13__bar i {
display: block;
block-size: 100%;
inline-size: var(--left, 50%);
background: currentColor;
transition: inline-size 0.6s linear;
}
.cdt-13__doc {
display: grid;
gap: 10px;
padding: 22px 8px 10px;
}
.cdt-13__doc span {
display: block;
block-size: 11px;
border-radius: 6px;
background: var(--rule);
}
.cdt-13__doc .cdt-13__h {
inline-size: 42%;
block-size: 18px;
}
.cdt-13__doc .cdt-13__short {
inline-size: 64%;
}
.cdt-13__note {
margin: 0;
font-size: 12px;
line-height: 1.65;
color: var(--muted);
text-wrap: pretty;
}
.cdt-13__note code {
padding: 1px 5px;
border-radius: 4px;
background: var(--rule);
font: 500 11px/1.4 ui-monospace, Menlo, monospace;
}
@media (prefers-reduced-motion: reduce) {
.cdt-13__bar i {
transition: none;
}
}
/* --- pure-CSS theme toggle ------------------------------------------- */
.cdt-13 {
position: relative;
}
.cdt-13__tgl {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.cdt-13__tglbtn {
position: absolute;
inset-block-end: 14px;
inset-inline-end: 14px;
z-index: 20;
display: inline-flex;
align-items: center;
gap: 7px;
min-block-size: 44px;
min-inline-size: 44px;
padding: 12px 14px;
border: 1px solid currentColor;
border-radius: 10px;
background: transparent;
color: inherit;
font: 600 11px/1 system-ui, sans-serif;
letter-spacing: 0.06em;
text-transform: uppercase;
cursor: pointer;
opacity: 0.62;
}
.cdt-13__tglbtn:hover {
opacity: 1;
}
.cdt-13__tgl:focus-visible + .cdt-13__tglbtn {
outline: 2px solid currentColor;
outline-offset: 2px;
opacity: 1;
}
.cdt-13__tglbtn svg {
flex: none;
}
.cdt-13__sun,
.cdt-13__lighttext {
display: none;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__moon,
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__darktext {
display: none;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__sun {
display: block;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__lighttext {
display: inline;
}
@media (max-width: 460px) {
.cdt-13__tglbtn {
padding: 12px;
}
.cdt-13__darktext,
.cdt-13__lighttext,
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__darktext,
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__lighttext {
display: none;
}
}
/* toggle in the system-light case */
@media (prefers-color-scheme: light) {
.cdt-13:has(.cdt-13__tgl:checked) {
--page: #100c1c;
--app: #181329;
--ink: #efeaff;
--muted: #a599cc;
--rule: #271f42;
--accent: #a684ff;
--crit: #ff5fa8;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__btn {
color: #100c1c;
background: var(--accent);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__banner[data-state="critical"] .cdt-13__btn {
color: #100c1c;
background: var(--crit);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__close:hover {
background: rgba(255, 255, 255, 0.08);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__bar {
background: rgba(255, 255, 255, 0.1);
}
}
@media (prefers-color-scheme: dark) {
.cdt-13 {
--page: #100c1c;
--app: #181329;
--ink: #efeaff;
--muted: #a599cc;
--rule: #271f42;
--accent: #a684ff;
--crit: #ff5fa8;
}
.cdt-13__btn {
color: #100c1c;
background: var(--accent);
}
.cdt-13__banner[data-state="critical"] .cdt-13__btn {
color: #100c1c;
background: var(--crit);
}
.cdt-13__close:hover {
background: rgba(255, 255, 255, 0.08);
}
.cdt-13__bar {
background: rgba(255, 255, 255, 0.1);
}
/* mirror flip-back: checked means the opposite of the system preference */
.cdt-13:has(.cdt-13__tgl:checked) {
--page: oklch(97% 0.014 300);
--app: oklch(100% 0 0);
--ink: oklch(23% 0.06 300);
--muted: oklch(50% 0.06 300);
--rule: oklch(92% 0.02 300);
--accent: oklch(52% 0.22 295);
--crit: oklch(56% 0.24 350);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__btn {
color: #fff;
background: var(--accent);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__banner[data-state="critical"] .cdt-13__btn {
color: #fff;
background: var(--crit);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__close:hover {
background: rgba(0, 0, 0, 0.06);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__bar {
background: rgba(0, 0, 0, 0.08);
}
}
[data-theme="dark"] .cdt-13 {
--page: #100c1c;
--app: #181329;
--ink: #efeaff;
--muted: #a599cc;
--rule: #271f42;
--accent: #a684ff;
--crit: #ff5fa8;
}
[data-theme="dark"] .cdt-13 .cdt-13__btn {
color: #100c1c;
}.cdt-13 {
--page: #f7f5fd;
--app: #ffffff;
--ink: #191430;
--muted: #675e8c;
--rule: #e6e1f5;
--accent: #6d3ce0;
--crit: #d61f7a;
--font-display: "Space Grotesk", "Geist", ui-sans-serif, system-ui, sans-serif;
width: 100%;
min-height: 100vh;
display: block;
box-sizing: border-box;
background: var(--page);
color: var(--ink);
font-family: var(--font-display);
}
@supports (color: oklch(50% 0.1 20)) {
.cdt-13 {
--page: oklch(97% 0.014 300);
--app: oklch(100% 0 0);
--ink: oklch(23% 0.06 300);
--muted: oklch(50% 0.06 300);
--rule: oklch(92% 0.02 300);
--accent: oklch(52% 0.22 295);
--crit: oklch(56% 0.24 350);
}
}
.cdt-13 *,
.cdt-13 *::before,
.cdt-13 *::after {
box-sizing: border-box;
}
.cdt-13__stage {
display: grid;
gap: 18px;
align-content: center;
min-height: 100vh;
max-inline-size: 880px;
margin-inline: auto;
padding: clamp(16px, 4vw, 48px);
}
.cdt-13__title {
margin: 0;
font: 600 clamp(18px, 3vw, 24px)/1.2 var(--font-display);
letter-spacing: -0.02em;
}
.cdt-13__app {
padding: 12px;
background: var(--app);
border: 1px solid var(--rule);
border-radius: 16px;
box-shadow: 0 20px 46px -30px rgba(109, 60, 224, 0.5);
}
.cdt-13__chrome {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 4px 10px;
padding: 6px 6px 14px;
border-block-end: 1px solid var(--rule);
}
.cdt-13__chrome span {
font: 700 14px/1 var(--font-display);
}
.cdt-13__chrome em {
font: 500 11.5px/1 var(--font-display);
font-style: normal;
color: var(--muted);
}
.cdt-13__wrap {
margin-block-start: 12px;
}
.cdt-13__x {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.cdt-13__wrap:has(.cdt-13__x:checked) .cdt-13__banner {
display: none;
}
.cdt-13__x:focus-visible + .cdt-13__banner .cdt-13__close {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.cdt-13__banner {
position: relative;
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 6px 10px;
padding: 12px 40px 14px 12px;
border-radius: 12px;
overflow: hidden;
background: rgba(109, 60, 224, 0.08);
border: 1px solid rgba(109, 60, 224, 0.24);
color: var(--accent);
}
@supports (color: color-mix(in oklab, red, blue)) {
.cdt-13__banner {
background: color-mix(in oklab, var(--accent) 9%, var(--app));
border-color: color-mix(in oklab, var(--accent) 28%, var(--app));
}
}
.cdt-13__banner[data-state="critical"] {
background: rgba(214, 31, 122, 0.1);
border-color: rgba(214, 31, 122, 0.3);
color: var(--crit);
}
@supports (color: color-mix(in oklab, red, blue)) {
.cdt-13__banner[data-state="critical"] {
background: color-mix(in oklab, var(--crit) 11%, var(--app));
border-color: color-mix(in oklab, var(--crit) 32%, var(--app));
}
}
.cdt-13__ico {
display: flex;
padding-block-start: 1px;
}
.cdt-13__copy {
grid-column: 2;
margin: 0;
min-inline-size: 0;
font-size: 13px;
line-height: 1.5;
color: var(--muted);
text-wrap: pretty;
}
.cdt-13__copy strong {
color: inherit;
font-weight: 600;
}
.cdt-13__banner[data-state="critical"] .cdt-13__copy strong {
font-weight: 700;
}
.cdt-13__copy time {
font-variant-numeric: tabular-nums;
}
.cdt-13__act {
grid-column: 2;
}
.cdt-13__btn {
min-block-size: 44px;
min-inline-size: 44px;
padding: 11px 16px;
border: 0;
border-radius: 9px;
background: var(--accent);
color: #fff;
font: 600 13px/1 var(--font-display);
cursor: pointer;
}
.cdt-13__banner[data-state="critical"] .cdt-13__btn {
background: var(--crit);
}
.cdt-13__btn:hover {
filter: brightness(1.08);
}
.cdt-13__btn:focus-visible {
outline: 2px solid var(--ink);
outline-offset: 2px;
}
.cdt-13__close {
position: absolute;
inset-block-start: 8px;
inset-inline-end: 8px;
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border-radius: 9px;
color: inherit;
cursor: pointer;
}
.cdt-13__close:hover {
background: rgba(0, 0, 0, 0.06);
}
.cdt-13__srlabel {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.cdt-13__bar {
position: absolute;
inset-block-end: 0;
inset-inline: 0;
block-size: 3px;
background: rgba(0, 0, 0, 0.08);
}
.cdt-13__bar i {
display: block;
block-size: 100%;
inline-size: var(--left, 50%);
background: currentColor;
transition: inline-size 0.6s linear;
}
.cdt-13__doc {
display: grid;
gap: 10px;
padding: 22px 8px 10px;
}
.cdt-13__doc span {
display: block;
block-size: 11px;
border-radius: 6px;
background: var(--rule);
}
.cdt-13__doc .cdt-13__h {
inline-size: 42%;
block-size: 18px;
}
.cdt-13__doc .cdt-13__short {
inline-size: 64%;
}
.cdt-13__note {
margin: 0;
font-size: 12px;
line-height: 1.65;
color: var(--muted);
text-wrap: pretty;
}
.cdt-13__note code {
padding: 1px 5px;
border-radius: 4px;
background: var(--rule);
font: 500 11px/1.4 ui-monospace, Menlo, monospace;
}
@media (prefers-reduced-motion: reduce) {
.cdt-13__bar i {
transition: none;
}
}
/* --- pure-CSS theme toggle ------------------------------------------- */
.cdt-13 {
position: relative;
}
.cdt-13__tgl {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.cdt-13__tglbtn {
position: absolute;
inset-block-end: 14px;
inset-inline-end: 14px;
z-index: 20;
display: inline-flex;
align-items: center;
gap: 7px;
min-block-size: 44px;
min-inline-size: 44px;
padding: 12px 14px;
border: 1px solid currentColor;
border-radius: 10px;
background: transparent;
color: inherit;
font: 600 11px/1 system-ui, sans-serif;
letter-spacing: 0.06em;
text-transform: uppercase;
cursor: pointer;
opacity: 0.62;
}
.cdt-13__tglbtn:hover {
opacity: 1;
}
.cdt-13__tgl:focus-visible + .cdt-13__tglbtn {
outline: 2px solid currentColor;
outline-offset: 2px;
opacity: 1;
}
.cdt-13__tglbtn svg {
flex: none;
}
.cdt-13__sun,
.cdt-13__lighttext {
display: none;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__moon,
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__darktext {
display: none;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__sun {
display: block;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__lighttext {
display: inline;
}
@media (max-width: 460px) {
.cdt-13__tglbtn {
padding: 12px;
}
.cdt-13__darktext,
.cdt-13__lighttext,
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__darktext,
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__lighttext {
display: none;
}
}
/* toggle in the system-light case */
@media (prefers-color-scheme: light) {
.cdt-13:has(.cdt-13__tgl:checked) {
--page: #100c1c;
--app: #181329;
--ink: #efeaff;
--muted: #a599cc;
--rule: #271f42;
--accent: #a684ff;
--crit: #ff5fa8;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__btn {
color: #100c1c;
background: var(--accent);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__banner[data-state="critical"] .cdt-13__btn {
color: #100c1c;
background: var(--crit);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__close:hover {
background: rgba(255, 255, 255, 0.08);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__bar {
background: rgba(255, 255, 255, 0.1);
}
}
@media (prefers-color-scheme: dark) {
.cdt-13 {
--page: #100c1c;
--app: #181329;
--ink: #efeaff;
--muted: #a599cc;
--rule: #271f42;
--accent: #a684ff;
--crit: #ff5fa8;
}
.cdt-13__btn {
color: #100c1c;
background: var(--accent);
}
.cdt-13__banner[data-state="critical"] .cdt-13__btn {
color: #100c1c;
background: var(--crit);
}
.cdt-13__close:hover {
background: rgba(255, 255, 255, 0.08);
}
.cdt-13__bar {
background: rgba(255, 255, 255, 0.1);
}
/* mirror flip-back: checked means the opposite of the system preference */
.cdt-13:has(.cdt-13__tgl:checked) {
--page: oklch(97% 0.014 300);
--app: oklch(100% 0 0);
--ink: oklch(23% 0.06 300);
--muted: oklch(50% 0.06 300);
--rule: oklch(92% 0.02 300);
--accent: oklch(52% 0.22 295);
--crit: oklch(56% 0.24 350);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__btn {
color: #fff;
background: var(--accent);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__banner[data-state="critical"] .cdt-13__btn {
color: #fff;
background: var(--crit);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__close:hover {
background: rgba(0, 0, 0, 0.06);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__bar {
background: rgba(0, 0, 0, 0.08);
}
}
[data-theme="dark"] .cdt-13 {
--page: #100c1c;
--app: #181329;
--ink: #efeaff;
--muted: #a599cc;
--rule: #271f42;
--accent: #a684ff;
--crit: #ff5fa8;
}
[data-theme="dark"] .cdt-13 .cdt-13__btn {
color: #100c1c;
}Here's a working CSS Countdown Timer from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Subscription Trial Expiry Countdown Banner
Source: https://codefronts.com/snippets/css-countdown-timers/subscription-trial-expiry-countdown-banner/
An in-app trial banner that escalates as the window closes: a calm purple state with seven days left, a magenta critical state inside the last two days, and a depleting bar in both. Dismissal is a checkbox and a :has() rule, so it costs no script — and this is an authenticated surface, not a site-wide promo bar.
## HTML
```html
<section class="cdt-13" aria-labelledby="cdt-13-title">
<input class="cdt-13__tgl" type="checkbox" id="cdt-13-theme">
<label class="cdt-13__tglbtn" for="cdt-13-theme">
<svg class="cdt-13__moon" viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><path d="M15.7 12.8A6.5 6.5 0 0 1 7.2 4.3a6.6 6.6 0 1 0 8.5 8.5Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>
<svg class="cdt-13__sun" viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><circle cx="10" cy="10" r="3.7" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 1.8v2.1M10 16.1v2.1M1.8 10h2.1M16.1 10h2.1M4.2 4.2l1.5 1.5M14.3 14.3l1.5 1.5M15.8 4.2l-1.5 1.5M5.7 14.3l-1.5 1.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
<span class="cdt-13__darktext">Dark mode</span><span class="cdt-13__lighttext">Light mode</span>
</label>
<div class="cdt-13__stage">
<h2 class="cdt-13__title" id="cdt-13-title">Trial banner · two escalation states</h2>
<div class="cdt-13__app">
<div class="cdt-13__chrome" aria-hidden="true"><span>Quill</span><em>Nova workspace · Alex Chen</em></div>
<div class="cdt-13__wrap">
<input class="cdt-13__x" type="checkbox" id="cdt-13-d1">
<div class="cdt-13__banner" data-state="calm" style="--left: 58%" role="timer">
<span class="cdt-13__ico" aria-hidden="true">
<svg viewBox="0 0 20 20" width="17" height="17"><circle cx="10" cy="10" r="7.6" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 6v4.3l2.8 1.7" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
</span>
<p class="cdt-13__copy"><strong><time datetime="P7D">7 days left</time> in your trial.</strong> Keep unlimited documents and version history by adding a plan.</p>
<div class="cdt-13__act"><button class="cdt-13__btn" type="button">Choose a plan</button></div>
<label class="cdt-13__close" for="cdt-13-d1" title="Dismiss for this session"><span class="cdt-13__srlabel">Dismiss trial banner for this session</span><svg viewBox="0 0 14 14" width="13" height="13" aria-hidden="true"><path d="M2.5 2.5l9 9M11.5 2.5l-9 9" stroke="currentColor" stroke-width="1.6"/></svg></label>
<div class="cdt-13__bar" aria-hidden="true"><i></i></div>
</div>
</div>
<div class="cdt-13__wrap">
<input class="cdt-13__x" type="checkbox" id="cdt-13-d2">
<div class="cdt-13__banner" data-state="critical" style="--left: 9%" role="timer">
<span class="cdt-13__ico" aria-hidden="true">
<svg viewBox="0 0 20 20" width="17" height="17"><path d="M10 2.4 1.6 16.6h16.8L10 2.4Z" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 7.4v4.1M10 13.4v1" stroke="currentColor" stroke-width="1.5"/></svg>
</span>
<p class="cdt-13__copy"><strong><time datetime="PT41H">41 hours left</time> — then editing locks.</strong> Your documents stay safe and read-only. No card on file yet.</p>
<div class="cdt-13__act"><button class="cdt-13__btn" type="button">Add payment method</button></div>
<label class="cdt-13__close" for="cdt-13-d2" title="Dismiss for this session"><span class="cdt-13__srlabel">Dismiss trial banner for this session</span><svg viewBox="0 0 14 14" width="13" height="13" aria-hidden="true"><path d="M2.5 2.5l9 9M11.5 2.5l-9 9" stroke="currentColor" stroke-width="1.6"/></svg></label>
<div class="cdt-13__bar" aria-hidden="true"><i></i></div>
</div>
</div>
<div class="cdt-13__doc" aria-hidden="true">
<span class="cdt-13__h"></span><span></span><span></span><span class="cdt-13__short"></span>
</div>
</div>
<p class="cdt-13__note">An authenticated in-app surface scoped to one workspace, not a site-wide promo bar. Dismissal is a checkbox plus a <code>:has()</code> rule, so it lasts for the session and costs no script.</p>
</div>
</section>
```
## CSS
```css
.cdt-13 {
--page: #f7f5fd;
--app: #ffffff;
--ink: #191430;
--muted: #675e8c;
--rule: #e6e1f5;
--accent: #6d3ce0;
--crit: #d61f7a;
--font-display: "Space Grotesk", "Geist", ui-sans-serif, system-ui, sans-serif;
width: 100%;
min-height: 100vh;
display: block;
box-sizing: border-box;
background: var(--page);
color: var(--ink);
font-family: var(--font-display);
}
@supports (color: oklch(50% 0.1 20)) {
.cdt-13 {
--page: oklch(97% 0.014 300);
--app: oklch(100% 0 0);
--ink: oklch(23% 0.06 300);
--muted: oklch(50% 0.06 300);
--rule: oklch(92% 0.02 300);
--accent: oklch(52% 0.22 295);
--crit: oklch(56% 0.24 350);
}
}
.cdt-13 *,
.cdt-13 *::before,
.cdt-13 *::after {
box-sizing: border-box;
}
.cdt-13__stage {
display: grid;
gap: 18px;
align-content: center;
min-height: 100vh;
max-inline-size: 880px;
margin-inline: auto;
padding: clamp(16px, 4vw, 48px);
}
.cdt-13__title {
margin: 0;
font: 600 clamp(18px, 3vw, 24px)/1.2 var(--font-display);
letter-spacing: -0.02em;
}
.cdt-13__app {
padding: 12px;
background: var(--app);
border: 1px solid var(--rule);
border-radius: 16px;
box-shadow: 0 20px 46px -30px rgba(109, 60, 224, 0.5);
}
.cdt-13__chrome {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 4px 10px;
padding: 6px 6px 14px;
border-block-end: 1px solid var(--rule);
}
.cdt-13__chrome span {
font: 700 14px/1 var(--font-display);
}
.cdt-13__chrome em {
font: 500 11.5px/1 var(--font-display);
font-style: normal;
color: var(--muted);
}
.cdt-13__wrap {
margin-block-start: 12px;
}
.cdt-13__x {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.cdt-13__wrap:has(.cdt-13__x:checked) .cdt-13__banner {
display: none;
}
.cdt-13__x:focus-visible + .cdt-13__banner .cdt-13__close {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.cdt-13__banner {
position: relative;
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 6px 10px;
padding: 12px 40px 14px 12px;
border-radius: 12px;
overflow: hidden;
background: rgba(109, 60, 224, 0.08);
border: 1px solid rgba(109, 60, 224, 0.24);
color: var(--accent);
}
@supports (color: color-mix(in oklab, red, blue)) {
.cdt-13__banner {
background: color-mix(in oklab, var(--accent) 9%, var(--app));
border-color: color-mix(in oklab, var(--accent) 28%, var(--app));
}
}
.cdt-13__banner[data-state="critical"] {
background: rgba(214, 31, 122, 0.1);
border-color: rgba(214, 31, 122, 0.3);
color: var(--crit);
}
@supports (color: color-mix(in oklab, red, blue)) {
.cdt-13__banner[data-state="critical"] {
background: color-mix(in oklab, var(--crit) 11%, var(--app));
border-color: color-mix(in oklab, var(--crit) 32%, var(--app));
}
}
.cdt-13__ico {
display: flex;
padding-block-start: 1px;
}
.cdt-13__copy {
grid-column: 2;
margin: 0;
min-inline-size: 0;
font-size: 13px;
line-height: 1.5;
color: var(--muted);
text-wrap: pretty;
}
.cdt-13__copy strong {
color: inherit;
font-weight: 600;
}
.cdt-13__banner[data-state="critical"] .cdt-13__copy strong {
font-weight: 700;
}
.cdt-13__copy time {
font-variant-numeric: tabular-nums;
}
.cdt-13__act {
grid-column: 2;
}
.cdt-13__btn {
min-block-size: 44px;
min-inline-size: 44px;
padding: 11px 16px;
border: 0;
border-radius: 9px;
background: var(--accent);
color: #fff;
font: 600 13px/1 var(--font-display);
cursor: pointer;
}
.cdt-13__banner[data-state="critical"] .cdt-13__btn {
background: var(--crit);
}
.cdt-13__btn:hover {
filter: brightness(1.08);
}
.cdt-13__btn:focus-visible {
outline: 2px solid var(--ink);
outline-offset: 2px;
}
.cdt-13__close {
position: absolute;
inset-block-start: 8px;
inset-inline-end: 8px;
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border-radius: 9px;
color: inherit;
cursor: pointer;
}
.cdt-13__close:hover {
background: rgba(0, 0, 0, 0.06);
}
.cdt-13__srlabel {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.cdt-13__bar {
position: absolute;
inset-block-end: 0;
inset-inline: 0;
block-size: 3px;
background: rgba(0, 0, 0, 0.08);
}
.cdt-13__bar i {
display: block;
block-size: 100%;
inline-size: var(--left, 50%);
background: currentColor;
transition: inline-size 0.6s linear;
}
.cdt-13__doc {
display: grid;
gap: 10px;
padding: 22px 8px 10px;
}
.cdt-13__doc span {
display: block;
block-size: 11px;
border-radius: 6px;
background: var(--rule);
}
.cdt-13__doc .cdt-13__h {
inline-size: 42%;
block-size: 18px;
}
.cdt-13__doc .cdt-13__short {
inline-size: 64%;
}
.cdt-13__note {
margin: 0;
font-size: 12px;
line-height: 1.65;
color: var(--muted);
text-wrap: pretty;
}
.cdt-13__note code {
padding: 1px 5px;
border-radius: 4px;
background: var(--rule);
font: 500 11px/1.4 ui-monospace, Menlo, monospace;
}
@media (prefers-reduced-motion: reduce) {
.cdt-13__bar i {
transition: none;
}
}
/* --- pure-CSS theme toggle ------------------------------------------- */
.cdt-13 {
position: relative;
}
.cdt-13__tgl {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.cdt-13__tglbtn {
position: absolute;
inset-block-end: 14px;
inset-inline-end: 14px;
z-index: 20;
display: inline-flex;
align-items: center;
gap: 7px;
min-block-size: 44px;
min-inline-size: 44px;
padding: 12px 14px;
border: 1px solid currentColor;
border-radius: 10px;
background: transparent;
color: inherit;
font: 600 11px/1 system-ui, sans-serif;
letter-spacing: 0.06em;
text-transform: uppercase;
cursor: pointer;
opacity: 0.62;
}
.cdt-13__tglbtn:hover {
opacity: 1;
}
.cdt-13__tgl:focus-visible + .cdt-13__tglbtn {
outline: 2px solid currentColor;
outline-offset: 2px;
opacity: 1;
}
.cdt-13__tglbtn svg {
flex: none;
}
.cdt-13__sun,
.cdt-13__lighttext {
display: none;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__moon,
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__darktext {
display: none;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__sun {
display: block;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__lighttext {
display: inline;
}
@media (max-width: 460px) {
.cdt-13__tglbtn {
padding: 12px;
}
.cdt-13__darktext,
.cdt-13__lighttext,
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__darktext,
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__lighttext {
display: none;
}
}
/* toggle in the system-light case */
@media (prefers-color-scheme: light) {
.cdt-13:has(.cdt-13__tgl:checked) {
--page: #100c1c;
--app: #181329;
--ink: #efeaff;
--muted: #a599cc;
--rule: #271f42;
--accent: #a684ff;
--crit: #ff5fa8;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__btn {
color: #100c1c;
background: var(--accent);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__banner[data-state="critical"] .cdt-13__btn {
color: #100c1c;
background: var(--crit);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__close:hover {
background: rgba(255, 255, 255, 0.08);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__bar {
background: rgba(255, 255, 255, 0.1);
}
}
@media (prefers-color-scheme: dark) {
.cdt-13 {
--page: #100c1c;
--app: #181329;
--ink: #efeaff;
--muted: #a599cc;
--rule: #271f42;
--accent: #a684ff;
--crit: #ff5fa8;
}
.cdt-13__btn {
color: #100c1c;
background: var(--accent);
}
.cdt-13__banner[data-state="critical"] .cdt-13__btn {
color: #100c1c;
background: var(--crit);
}
.cdt-13__close:hover {
background: rgba(255, 255, 255, 0.08);
}
.cdt-13__bar {
background: rgba(255, 255, 255, 0.1);
}
/* mirror flip-back: checked means the opposite of the system preference */
.cdt-13:has(.cdt-13__tgl:checked) {
--page: oklch(97% 0.014 300);
--app: oklch(100% 0 0);
--ink: oklch(23% 0.06 300);
--muted: oklch(50% 0.06 300);
--rule: oklch(92% 0.02 300);
--accent: oklch(52% 0.22 295);
--crit: oklch(56% 0.24 350);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__btn {
color: #fff;
background: var(--accent);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__banner[data-state="critical"] .cdt-13__btn {
color: #fff;
background: var(--crit);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__close:hover {
background: rgba(0, 0, 0, 0.06);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__bar {
background: rgba(0, 0, 0, 0.08);
}
}
[data-theme="dark"] .cdt-13 {
--page: #100c1c;
--app: #181329;
--ink: #efeaff;
--muted: #a599cc;
--rule: #271f42;
--accent: #a684ff;
--crit: #ff5fa8;
}
[data-theme="dark"] .cdt-13 .cdt-13__btn {
color: #100c1c;
}
```Here's a working CSS Countdown Timer from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Subscription Trial Expiry Countdown Banner
Source: https://codefronts.com/snippets/css-countdown-timers/subscription-trial-expiry-countdown-banner/
An in-app trial banner that escalates as the window closes: a calm purple state with seven days left, a magenta critical state inside the last two days, and a depleting bar in both. Dismissal is a checkbox and a :has() rule, so it costs no script — and this is an authenticated surface, not a site-wide promo bar.
## HTML
```html
<section class="cdt-13" aria-labelledby="cdt-13-title">
<input class="cdt-13__tgl" type="checkbox" id="cdt-13-theme">
<label class="cdt-13__tglbtn" for="cdt-13-theme">
<svg class="cdt-13__moon" viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><path d="M15.7 12.8A6.5 6.5 0 0 1 7.2 4.3a6.6 6.6 0 1 0 8.5 8.5Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>
<svg class="cdt-13__sun" viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><circle cx="10" cy="10" r="3.7" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 1.8v2.1M10 16.1v2.1M1.8 10h2.1M16.1 10h2.1M4.2 4.2l1.5 1.5M14.3 14.3l1.5 1.5M15.8 4.2l-1.5 1.5M5.7 14.3l-1.5 1.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
<span class="cdt-13__darktext">Dark mode</span><span class="cdt-13__lighttext">Light mode</span>
</label>
<div class="cdt-13__stage">
<h2 class="cdt-13__title" id="cdt-13-title">Trial banner · two escalation states</h2>
<div class="cdt-13__app">
<div class="cdt-13__chrome" aria-hidden="true"><span>Quill</span><em>Nova workspace · Alex Chen</em></div>
<div class="cdt-13__wrap">
<input class="cdt-13__x" type="checkbox" id="cdt-13-d1">
<div class="cdt-13__banner" data-state="calm" style="--left: 58%" role="timer">
<span class="cdt-13__ico" aria-hidden="true">
<svg viewBox="0 0 20 20" width="17" height="17"><circle cx="10" cy="10" r="7.6" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 6v4.3l2.8 1.7" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
</span>
<p class="cdt-13__copy"><strong><time datetime="P7D">7 days left</time> in your trial.</strong> Keep unlimited documents and version history by adding a plan.</p>
<div class="cdt-13__act"><button class="cdt-13__btn" type="button">Choose a plan</button></div>
<label class="cdt-13__close" for="cdt-13-d1" title="Dismiss for this session"><span class="cdt-13__srlabel">Dismiss trial banner for this session</span><svg viewBox="0 0 14 14" width="13" height="13" aria-hidden="true"><path d="M2.5 2.5l9 9M11.5 2.5l-9 9" stroke="currentColor" stroke-width="1.6"/></svg></label>
<div class="cdt-13__bar" aria-hidden="true"><i></i></div>
</div>
</div>
<div class="cdt-13__wrap">
<input class="cdt-13__x" type="checkbox" id="cdt-13-d2">
<div class="cdt-13__banner" data-state="critical" style="--left: 9%" role="timer">
<span class="cdt-13__ico" aria-hidden="true">
<svg viewBox="0 0 20 20" width="17" height="17"><path d="M10 2.4 1.6 16.6h16.8L10 2.4Z" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 7.4v4.1M10 13.4v1" stroke="currentColor" stroke-width="1.5"/></svg>
</span>
<p class="cdt-13__copy"><strong><time datetime="PT41H">41 hours left</time> — then editing locks.</strong> Your documents stay safe and read-only. No card on file yet.</p>
<div class="cdt-13__act"><button class="cdt-13__btn" type="button">Add payment method</button></div>
<label class="cdt-13__close" for="cdt-13-d2" title="Dismiss for this session"><span class="cdt-13__srlabel">Dismiss trial banner for this session</span><svg viewBox="0 0 14 14" width="13" height="13" aria-hidden="true"><path d="M2.5 2.5l9 9M11.5 2.5l-9 9" stroke="currentColor" stroke-width="1.6"/></svg></label>
<div class="cdt-13__bar" aria-hidden="true"><i></i></div>
</div>
</div>
<div class="cdt-13__doc" aria-hidden="true">
<span class="cdt-13__h"></span><span></span><span></span><span class="cdt-13__short"></span>
</div>
</div>
<p class="cdt-13__note">An authenticated in-app surface scoped to one workspace, not a site-wide promo bar. Dismissal is a checkbox plus a <code>:has()</code> rule, so it lasts for the session and costs no script.</p>
</div>
</section>
```
## CSS
```css
.cdt-13 {
--page: #f7f5fd;
--app: #ffffff;
--ink: #191430;
--muted: #675e8c;
--rule: #e6e1f5;
--accent: #6d3ce0;
--crit: #d61f7a;
--font-display: "Space Grotesk", "Geist", ui-sans-serif, system-ui, sans-serif;
width: 100%;
min-height: 100vh;
display: block;
box-sizing: border-box;
background: var(--page);
color: var(--ink);
font-family: var(--font-display);
}
@supports (color: oklch(50% 0.1 20)) {
.cdt-13 {
--page: oklch(97% 0.014 300);
--app: oklch(100% 0 0);
--ink: oklch(23% 0.06 300);
--muted: oklch(50% 0.06 300);
--rule: oklch(92% 0.02 300);
--accent: oklch(52% 0.22 295);
--crit: oklch(56% 0.24 350);
}
}
.cdt-13 *,
.cdt-13 *::before,
.cdt-13 *::after {
box-sizing: border-box;
}
.cdt-13__stage {
display: grid;
gap: 18px;
align-content: center;
min-height: 100vh;
max-inline-size: 880px;
margin-inline: auto;
padding: clamp(16px, 4vw, 48px);
}
.cdt-13__title {
margin: 0;
font: 600 clamp(18px, 3vw, 24px)/1.2 var(--font-display);
letter-spacing: -0.02em;
}
.cdt-13__app {
padding: 12px;
background: var(--app);
border: 1px solid var(--rule);
border-radius: 16px;
box-shadow: 0 20px 46px -30px rgba(109, 60, 224, 0.5);
}
.cdt-13__chrome {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 4px 10px;
padding: 6px 6px 14px;
border-block-end: 1px solid var(--rule);
}
.cdt-13__chrome span {
font: 700 14px/1 var(--font-display);
}
.cdt-13__chrome em {
font: 500 11.5px/1 var(--font-display);
font-style: normal;
color: var(--muted);
}
.cdt-13__wrap {
margin-block-start: 12px;
}
.cdt-13__x {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.cdt-13__wrap:has(.cdt-13__x:checked) .cdt-13__banner {
display: none;
}
.cdt-13__x:focus-visible + .cdt-13__banner .cdt-13__close {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.cdt-13__banner {
position: relative;
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 6px 10px;
padding: 12px 40px 14px 12px;
border-radius: 12px;
overflow: hidden;
background: rgba(109, 60, 224, 0.08);
border: 1px solid rgba(109, 60, 224, 0.24);
color: var(--accent);
}
@supports (color: color-mix(in oklab, red, blue)) {
.cdt-13__banner {
background: color-mix(in oklab, var(--accent) 9%, var(--app));
border-color: color-mix(in oklab, var(--accent) 28%, var(--app));
}
}
.cdt-13__banner[data-state="critical"] {
background: rgba(214, 31, 122, 0.1);
border-color: rgba(214, 31, 122, 0.3);
color: var(--crit);
}
@supports (color: color-mix(in oklab, red, blue)) {
.cdt-13__banner[data-state="critical"] {
background: color-mix(in oklab, var(--crit) 11%, var(--app));
border-color: color-mix(in oklab, var(--crit) 32%, var(--app));
}
}
.cdt-13__ico {
display: flex;
padding-block-start: 1px;
}
.cdt-13__copy {
grid-column: 2;
margin: 0;
min-inline-size: 0;
font-size: 13px;
line-height: 1.5;
color: var(--muted);
text-wrap: pretty;
}
.cdt-13__copy strong {
color: inherit;
font-weight: 600;
}
.cdt-13__banner[data-state="critical"] .cdt-13__copy strong {
font-weight: 700;
}
.cdt-13__copy time {
font-variant-numeric: tabular-nums;
}
.cdt-13__act {
grid-column: 2;
}
.cdt-13__btn {
min-block-size: 44px;
min-inline-size: 44px;
padding: 11px 16px;
border: 0;
border-radius: 9px;
background: var(--accent);
color: #fff;
font: 600 13px/1 var(--font-display);
cursor: pointer;
}
.cdt-13__banner[data-state="critical"] .cdt-13__btn {
background: var(--crit);
}
.cdt-13__btn:hover {
filter: brightness(1.08);
}
.cdt-13__btn:focus-visible {
outline: 2px solid var(--ink);
outline-offset: 2px;
}
.cdt-13__close {
position: absolute;
inset-block-start: 8px;
inset-inline-end: 8px;
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border-radius: 9px;
color: inherit;
cursor: pointer;
}
.cdt-13__close:hover {
background: rgba(0, 0, 0, 0.06);
}
.cdt-13__srlabel {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.cdt-13__bar {
position: absolute;
inset-block-end: 0;
inset-inline: 0;
block-size: 3px;
background: rgba(0, 0, 0, 0.08);
}
.cdt-13__bar i {
display: block;
block-size: 100%;
inline-size: var(--left, 50%);
background: currentColor;
transition: inline-size 0.6s linear;
}
.cdt-13__doc {
display: grid;
gap: 10px;
padding: 22px 8px 10px;
}
.cdt-13__doc span {
display: block;
block-size: 11px;
border-radius: 6px;
background: var(--rule);
}
.cdt-13__doc .cdt-13__h {
inline-size: 42%;
block-size: 18px;
}
.cdt-13__doc .cdt-13__short {
inline-size: 64%;
}
.cdt-13__note {
margin: 0;
font-size: 12px;
line-height: 1.65;
color: var(--muted);
text-wrap: pretty;
}
.cdt-13__note code {
padding: 1px 5px;
border-radius: 4px;
background: var(--rule);
font: 500 11px/1.4 ui-monospace, Menlo, monospace;
}
@media (prefers-reduced-motion: reduce) {
.cdt-13__bar i {
transition: none;
}
}
/* --- pure-CSS theme toggle ------------------------------------------- */
.cdt-13 {
position: relative;
}
.cdt-13__tgl {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.cdt-13__tglbtn {
position: absolute;
inset-block-end: 14px;
inset-inline-end: 14px;
z-index: 20;
display: inline-flex;
align-items: center;
gap: 7px;
min-block-size: 44px;
min-inline-size: 44px;
padding: 12px 14px;
border: 1px solid currentColor;
border-radius: 10px;
background: transparent;
color: inherit;
font: 600 11px/1 system-ui, sans-serif;
letter-spacing: 0.06em;
text-transform: uppercase;
cursor: pointer;
opacity: 0.62;
}
.cdt-13__tglbtn:hover {
opacity: 1;
}
.cdt-13__tgl:focus-visible + .cdt-13__tglbtn {
outline: 2px solid currentColor;
outline-offset: 2px;
opacity: 1;
}
.cdt-13__tglbtn svg {
flex: none;
}
.cdt-13__sun,
.cdt-13__lighttext {
display: none;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__moon,
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__darktext {
display: none;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__sun {
display: block;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__lighttext {
display: inline;
}
@media (max-width: 460px) {
.cdt-13__tglbtn {
padding: 12px;
}
.cdt-13__darktext,
.cdt-13__lighttext,
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__darktext,
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__lighttext {
display: none;
}
}
/* toggle in the system-light case */
@media (prefers-color-scheme: light) {
.cdt-13:has(.cdt-13__tgl:checked) {
--page: #100c1c;
--app: #181329;
--ink: #efeaff;
--muted: #a599cc;
--rule: #271f42;
--accent: #a684ff;
--crit: #ff5fa8;
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__btn {
color: #100c1c;
background: var(--accent);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__banner[data-state="critical"] .cdt-13__btn {
color: #100c1c;
background: var(--crit);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__close:hover {
background: rgba(255, 255, 255, 0.08);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__bar {
background: rgba(255, 255, 255, 0.1);
}
}
@media (prefers-color-scheme: dark) {
.cdt-13 {
--page: #100c1c;
--app: #181329;
--ink: #efeaff;
--muted: #a599cc;
--rule: #271f42;
--accent: #a684ff;
--crit: #ff5fa8;
}
.cdt-13__btn {
color: #100c1c;
background: var(--accent);
}
.cdt-13__banner[data-state="critical"] .cdt-13__btn {
color: #100c1c;
background: var(--crit);
}
.cdt-13__close:hover {
background: rgba(255, 255, 255, 0.08);
}
.cdt-13__bar {
background: rgba(255, 255, 255, 0.1);
}
/* mirror flip-back: checked means the opposite of the system preference */
.cdt-13:has(.cdt-13__tgl:checked) {
--page: oklch(97% 0.014 300);
--app: oklch(100% 0 0);
--ink: oklch(23% 0.06 300);
--muted: oklch(50% 0.06 300);
--rule: oklch(92% 0.02 300);
--accent: oklch(52% 0.22 295);
--crit: oklch(56% 0.24 350);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__btn {
color: #fff;
background: var(--accent);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__banner[data-state="critical"] .cdt-13__btn {
color: #fff;
background: var(--crit);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__close:hover {
background: rgba(0, 0, 0, 0.06);
}
.cdt-13:has(.cdt-13__tgl:checked) .cdt-13__bar {
background: rgba(0, 0, 0, 0.08);
}
}
[data-theme="dark"] .cdt-13 {
--page: #100c1c;
--app: #181329;
--ink: #efeaff;
--muted: #a599cc;
--rule: #271f42;
--accent: #a684ff;
--crit: #ff5fa8;
}
[data-theme="dark"] .cdt-13 .cdt-13__btn {
color: #100c1c;
}
```How this works
Two states, one component. A trial banner that looks identical on day seven and day one has trained the user to ignore it by the time it matters. The escalation is a single data-state attribute: calm gets the purple ground, the neutral verb and a quiet bar; critical gets the magenta tint, a heavier weight, a sharper sentence and the payment-method reassurance that removes the last objection. Both are rendered here so the difference is visible side by side.
Dismissal without script. A visually hidden checkbox sits before the banner, the close control is its <label>, and .cdt-13__wrap:has(:checked) .cdt-13__banner { display: none } removes it. The checkbox stays in the accessibility tree with a real label, so it is focusable and announced — hiding it with display: none would make the close button unreachable by keyboard, which is the classic version of this bug.
Session-scoped, not permanent. A CSS-only dismissal lasts as long as the DOM does: navigate and it returns. That is the correct behaviour for a trial notice — a banner about billing should not be permanently dismissible with one click — but it is a limitation to know, and persisting a dismissal beyond the session needs storage, which needs script.
This lane matters. A site-wide promo bar and an in-app trial notice look similar and behave differently: the promo bar is anonymous, marketing-owned and lives above the chrome; this banner is authenticated, product-owned, scoped to one workspace and states a real billing consequence. Keeping them separate is why this one sits inside the app frame with a workspace name on it. The bar depletes over the trial window using a transform, and prefers-reduced-motion pins it to its current width rather than removing the indicator.
Make it yours
- Switch a banner between states by changing
data-statefromcalmtocritical. - Set the bar's remaining fraction with the
--leftcustom property on the banner. - Retint the calm and critical grounds via
--accentand--crit. - Change the escalation threshold in your own render logic — 48 hours is the common default.
- Swap the upgrade CTA for a Talk to sales link by editing the action markup.
- Remove the dismissal by deleting the checkbox, label and the
:has()rule together. - Drop the theme toggle by deleting the checkbox, its
<label>and every:has(.cdt-13__tgl:checked)rule.
Gotchas — read before shipping
- Hiding the dismissal checkbox with
display: nonetakes the close control out of the tab order — use the clip-rect technique. - A CSS-only dismissal resets on navigation; persisting it needs storage and therefore script.
- One flat state for the whole trial trains users to ignore the banner before the day it matters.
- Escalating with colour alone fails WCAG 1.4.1; the critical state also changes the icon and the sentence.
- Shipping this as a site-wide promo bar confuses an authenticated billing notice with marketing chrome.
- A theme toggle written in only one colour-scheme media block inverts correctly for one system preference and does nothing for the other — the flip-back rule has to exist in both.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 114+ | 17.5+ | 121+ | 114+ |
Floor set by :has(), oklch(), color-mix(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.
The pure-CSS theme toggle uses :has(:checked), which puts the Firefox floor at 121 (Chrome 105, Safari 15.4); where :has() is missing the toggle button simply does nothing and the demo follows prefers-color-scheme alone. Two features set the floor: :has() for the script-free dismissal (Firefox 121) and color-mix(in oklab) for the critical tint (Safari 16.2). Older engines take the rgba() tints declared first and keep both states; without :has() the close control simply does nothing and the banner stays visible, which is a safe failure for a billing notice.