2026-10-06 22:40:28 +02:00
|
|
|
/* Touch screens.
|
|
|
|
|
|
|
|
|
|
A phone's layout uses the width variants (max-md:, md:) at the call site; how big a thing is to a finger uses the
|
2026-10-06 22:42:20 +02:00
|
|
|
pointer: the sizes here sit under @media (pointer: coarse), so a mouse matches none of them and desktop keeps
|
2026-10-06 22:40:28 +02:00
|
|
|
daisyUI's sizes. The rules are plain CSS in Tailwind's utilities layer: like neo.css's @utility overrides they beat
|
2026-10-06 22:42:20 +02:00
|
|
|
daisyUI's rules, which sit in sublayers of it, and they set daisyUI's own size variables where it has one.
|
|
|
|
|
|
|
|
|
|
A target gets a 44px hit area (Apple's guideline, WCAG 2.5.5), which tests/e2e/Mobile measures. Buttons, tabs and
|
|
|
|
|
fields are 44px tall; the extra small buttons and the badges that act stay 36px to the eye and reach 44px through a
|
|
|
|
|
transparent ::after, so the rows that hold them leave 8px between them on touch (pointer-coarse:gap-2), or two hit
|
|
|
|
|
areas would overlap. */
|
2026-10-06 23:13:23 +02:00
|
|
|
/* A small text link or button (a name, "edited") keeps its size to the eye and gets a 44px hit area, a transparent box
|
|
|
|
|
centred on it, on touch only. Not for one that truncates its text: its overflow clips the box, so such a link takes
|
|
|
|
|
padding instead (pointer-coarse:py-3.5). */
|
|
|
|
|
@utility touch-target {
|
|
|
|
|
@media (pointer: coarse) {
|
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
|
|
&::after {
|
|
|
|
|
content: "";
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 50%;
|
|
|
|
|
top: 50%;
|
|
|
|
|
width: max(100%, 2.75rem);
|
|
|
|
|
height: max(100%, 2.75rem);
|
|
|
|
|
translate: -50% -50%;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-06 23:23:14 +02:00
|
|
|
/* A short window: a phone held sideways. The globe's banner would leave the feed no room there. */
|
|
|
|
|
@custom-variant short (@media (max-height: 30rem));
|
|
|
|
|
|
2026-10-06 22:40:28 +02:00
|
|
|
@layer utilities {
|
2026-10-06 22:42:20 +02:00
|
|
|
/* At every width: a checkbox's, radio's or toggle's label wraps (daisyUI's .label never does, and a long one ran past
|
|
|
|
|
a phone's edge), and the control keeps its size beside it. */
|
|
|
|
|
.label:has(> input[type=checkbox], > input[type=radio], > .toggle) {
|
|
|
|
|
white-space: normal;
|
|
|
|
|
text-align: start;
|
|
|
|
|
|
|
|
|
|
& > input {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-06 22:40:28 +02:00
|
|
|
@media (pointer: coarse) {
|
|
|
|
|
/* Field text at 16px: iOS zooms into a focused field whose text is smaller, now that zooming is allowed. */
|
|
|
|
|
:where(.input, .select, .textarea) {
|
|
|
|
|
--font-size-min: 1rem;
|
|
|
|
|
}
|
2026-10-06 22:42:20 +02:00
|
|
|
|
|
|
|
|
.btn:not(.btn-xs, .btn-lg, .btn-xl) {
|
|
|
|
|
--size: 2.75rem;
|
|
|
|
|
min-width: 2.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-sm {
|
|
|
|
|
--fontsize: .875rem;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-06 23:13:23 +02:00
|
|
|
/* (the ::after is placed against the padding box, 34px inside a 1px border: 5px around it makes 44) */
|
2026-10-06 22:42:20 +02:00
|
|
|
.btn-xs,
|
|
|
|
|
:is(button, a).badge {
|
|
|
|
|
--size: 2.25rem;
|
|
|
|
|
min-width: 2.25rem;
|
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
|
|
&::after {
|
|
|
|
|
content: "";
|
|
|
|
|
position: absolute;
|
2026-10-06 23:13:23 +02:00
|
|
|
inset: -5px;
|
2026-10-06 22:42:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-06 23:13:23 +02:00
|
|
|
.btn-xs {
|
|
|
|
|
--fontsize: .875rem;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-06 22:42:20 +02:00
|
|
|
:is(button, a).badge {
|
|
|
|
|
padding-inline: .75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tabs {
|
|
|
|
|
--tab-height: 2.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab {
|
|
|
|
|
min-width: 2.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.input:not(.input-lg, .input-xl),
|
|
|
|
|
.select:not(.select-lg, .select-xl) {
|
|
|
|
|
--size: 2.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.checkbox,
|
|
|
|
|
.radio {
|
|
|
|
|
--size: 1.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toggle {
|
|
|
|
|
--size: 1.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.range {
|
|
|
|
|
--range-thumb-size: 1.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.collapse-title,
|
|
|
|
|
.label:has(> input[type=checkbox], > input[type=radio], > .toggle) {
|
|
|
|
|
min-height: 2.75rem;
|
|
|
|
|
}
|
2026-10-06 22:40:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
2026-10-06 23:31:07 +02:00
|
|
|
|
|
|
|
|
/* A sideways strip fades at the edge where more of it waits: the right edge at first, both while scrolled, the left
|
|
|
|
|
at the end (a scroll-driven animation where the browser has one; without, the right edge only). */
|
|
|
|
|
@property --fade-start {
|
|
|
|
|
syntax: "<length>";
|
|
|
|
|
inherits: false;
|
|
|
|
|
initial-value: 0px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@property --fade-end {
|
|
|
|
|
syntax: "<length>";
|
|
|
|
|
inherits: false;
|
|
|
|
|
initial-value: 1.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes edge-fade-x {
|
|
|
|
|
from {
|
|
|
|
|
--fade-start: 0px;
|
|
|
|
|
--fade-end: 1.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
8% {
|
|
|
|
|
--fade-start: 1.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
92% {
|
|
|
|
|
--fade-end: 1.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
to {
|
|
|
|
|
--fade-start: 1.5rem;
|
|
|
|
|
--fade-end: 0px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@utility edge-fade-x {
|
|
|
|
|
mask-image: linear-gradient(to right, transparent, #000 var(--fade-start), #000 calc(100% - var(--fade-end)), transparent);
|
|
|
|
|
|
|
|
|
|
@supports (animation-timeline: scroll()) {
|
|
|
|
|
animation: edge-fade-x linear both;
|
|
|
|
|
animation-timeline: scroll(self inline);
|
|
|
|
|
}
|
|
|
|
|
}
|