diff --git a/src/pages/css/index.md b/src/pages/css/index.md index 79243127..084b0534 100644 --- a/src/pages/css/index.md +++ b/src/pages/css/index.md @@ -9570,33 +9570,145 @@ Subgrid можно включить только по одной axis: **Короткий ответ** -Если нужен только display, можно использовать custom property и overlay. Для интерактивного rating нужны настоящие -controls, keyboard support и доступное имя. +Для read-only rating можно передать значение через custom property и визуально заполнить часть строки звезд gradient. +Если rating интерактивный, CSS-only display уже недостаточен: нужны настоящие form controls, keyboard interaction, +accessible name и понятное состояние для screen reader. **Полный ответ** -Если нужен только display, можно использовать custom property и overlay. Для интерактивного rating нужны настоящие -controls, keyboard support и доступное имя. +Сначала нужно уточнить requirement: это **только отображение оценки** или пользователь должен ее менять. + +Для read-only варианта можно оставить в DOM обычный текст для accessibility, а визуальную часть построить отдельно: + +```html + +``` ```css .rating { - --rating: 3.5; - --percent: calc(var(--rating) / 5 * 100%); + --max-rating: 5; + --percent: calc(var(--rating) / var(--max-rating) * 100%); + display: inline-block; + position: relative; font-size: 1.25rem; line-height: 1; } -.rating::before { +.rating > span { + color: #c8c8c8; +} + +.rating::after { content: '★★★★★'; + position: absolute; + inset: 0; + inline-size: var(--percent); + overflow: hidden; + color: currentColor; + white-space: nowrap; + pointer-events: none; +} +``` + +Здесь `--rating` задает data-driven value, а overlay показывает только нужную долю заполненных звезд. + +Можно сделать и через text gradient: + +```css +.rating > span { background: linear-gradient(90deg, currentColor var(--percent), #c8c8c8 var(--percent)); background-clip: text; color: transparent; } ``` -Частая ошибка - сделать красивый виджет, но потерять accessibility. Для ввода рейтинга лучше использовать radio group -или button group, а не только pseudo-elements. +Но важно помнить: gradient — presentation. Значение `3.5 из 5` должно оставаться доступным независимо от CSS. + +Для fractional rating есть edge cases: + +- значение нужно clamp-ить в диапазон `0..5` до передачи в style; +- при RTL direction простой gradient слева направо может заполнять звезды с неправильной стороны; +- icon font может иметь нестабильные metrics, поэтому SVG/icons часто предсказуемее; +- custom font со звездами не должен быть единственным носителем значения; +- нужно проверить forced colors/high contrast mode. + +Например для RTL можно менять направление gradient: + +```css +[dir='rtl'] .rating > span { + background-image: linear-gradient(270deg, currentColor var(--percent), #c8c8c8 var(--percent)); +} +``` + +Если задача **интерактивная**, pseudo-elements недостаточно. Нужен semantic input, например radio group: + +```html +
+``` + +Затем CSS может визуально превратить controls в звезды, но keyboard navigation, checked state и form semantics уже +обеспечены нативными controls. + +На интервью полезно проговорить ход решения: + +1. определить read-only или interactive requirement; +2. отделить semantic value от decorative stars; +3. не хранить business value только в CSS; +4. проверить fractional values, RTL и accessibility; +5. для interactive variant использовать native controls, а CSS оставить presentation layer. + +Сильный ответ — не просто показать gradient, а объяснить, **почему CSS-only подходит для display и где его граница как +interaction model**. @@ -9608,11 +9720,13 @@ controls, keyboard support и доступное имя. **Короткий ответ** -Что проверяет: Grid, responsive layout, минимальные размеры, отсутствие layout shift. +Для content-driven responsive grid подходит CSS Grid с `repeat(auto-fit, minmax(...))`: количество колонок меняется без +JavaScript и без жестких breakpoints. Нужно ограничить minimum track size так, чтобы grid не переполнял узкий container, +и отдельно обеспечить стабильные размеры media/content. **Полный ответ** -**Что проверяет:** Grid, responsive layout, минимальные размеры, отсутствие layout shift. +Для начала можно решить задачу без media queries: ```css .cards { @@ -9622,12 +9736,108 @@ controls, keyboard support и доступное имя. } .card { - min-width: 0; + min-inline-size: 0; } ``` -На интервью важно объяснить, почему `minmax(min(100%, 18rem), 1fr)` не переполняет узкий viewport, чем `auto-fit` -отличается от `auto-fill`, и как заранее задать размеры media через `aspect-ratio`. +Главная часть: + +```css +repeat(auto-fit, minmax(min(100%, 18rem), 1fr)) +``` + +означает: + +- `18rem` — желаемая minimum ширина карточки; +- `min(100%, 18rem)` не дает minimum track стать шире самого container; +- `1fr` позволяет доступному месту распределяться между колонками; +- `auto-fit` создает столько tracks, сколько помещается, и схлопывает пустые. + +Поэтому layout естественно переходит, например, от четырех колонок к трем, двум и одной в зависимости от **реальной +ширины container**, а не от заранее выбранного viewport breakpoint. + +Это особенно полезно для reusable component: один и тот же grid может находиться и на full-width page, и в sidebar. + +Если container-relative behavior важен еще сильнее, можно добавить container query для деталей карточки: + +```css +.cards { + container-type: inline-size; +} + +@container (width < 32rem) { + .card { + grid-template-columns: auto 1fr; + } +} +``` + +Но сама раскладка колонок в простом случае уже решается Grid без JavaScript и без container query. + +Почему не просто: + +```css +grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); +``` + +На container уже `18rem` minimum track может вызвать horizontal overflow. Обертка `min(100%, 18rem)` разрешает track +сжаться до container width. + +Еще один common edge case — intrinsic minimum size descendants. Длинный URL, code или `white-space: nowrap` может +растянуть grid item. Поэтому у внутренних flex/grid children часто нужен: + +```css +.card__content { + min-inline-size: 0; +} +``` + +и корректная стратегия wrapping: + +```css +.card__title { + overflow-wrap: anywhere; +} +``` + +Для media нужно заранее резервировать место, чтобы изображения не создавали layout shift: + +```css +.card__media { + aspect-ratio: 16 / 9; + overflow: hidden; +} + +.card__media img { + inline-size: 100%; + block-size: 100%; + object-fit: cover; +} +``` + +Еще важно понимать `auto-fit` vs `auto-fill`: + +- `auto-fill` сохраняет потенциальные пустые tracks; +- `auto-fit` схлопывает пустые tracks, поэтому существующие cards чаще растягиваются на освободившееся место. + +Например при трех карточках в широком container `auto-fit` может растянуть эти три columns, а `auto-fill` продолжит +учитывать место под потенциальную четвертую. + +При выборе между Grid и Flexbox здесь Grid обычно лучше, потому что задача двумерная: cards должны формировать +согласованные rows/columns. Flexbox подходит, если нужен прежде всего одномерный flow и independent item widths. + +Что стоит проверить: + +- container шире и уже minimum card size; +- очень длинный title/content; +- одна карточка и неполная последняя row; +- изображения до/после загрузки; +- zoom и увеличенный text; +- RTL, если карточка содержит directional layout; +- отсутствие horizontal scroll на mobile. + +На интервью сильный ответ выглядит так: **сначала дать рабочий Grid rule, затем объяснить `auto-fit`, `minmax()`, +intrinsic sizing и layout-shift prevention, а не просто воспроизвести snippet по памяти**.