18 Kindred Souls Project «Eternity»

    Kindred Souls

    Объявление

    Ночь не принесла девушке сновидений, самым нереальным показалось пробуждение. Ведьма заворочалась, потянула затёкшие ноги и вытянула шею. Волнующий родной запах пощекотал её нос. Мия потерлась о душистую кожу не открывая глаз. Захотелось то ли укусить то ли поцеловать. На автомате, сонные губы чмокнули что попало под них. Тогда Найтрей приоткрыла глаз и улыбнулась.
    — Привееет, — пропела она слегка охрипшим голосом. Мрак прошлого вечера не вспоминался и не портил ей настроение. Сон отсёк всё плохое, обновил мысли и перезагрузил аватар.
    Однако постепенно сознание включалось в работу, сообщая, что происходящее вполне реально. За ночь парочка устроилась вдоль дивана, Мия фактически лежала на фамильяре, у него был обнажён торс, она только что чмокнула его. От понимания, девушка резко проснулась и приподнялась над ним. Глаза распахнулись, проморгались, заметили довольного всем происходящим парня.
    “Мы провели вместе ночь”
    — Ой, — опешила и завертелась, топчась на фамильяре. В панике и спешке ретироваться, она зацепилась ногой и свалилась с дивана. — Ай! — пятая точка не обрадовалась, но Эмилия засмеялась. Немного нервно и неловко. — С добрым утром. Где мой кофе? — попыталась заговорить ему зубы она. Вода и пламя, клюв и когти

    Информация о пользователе

    Привет, Гость! Войдите или зарегистрируйтесь.


    Вы здесь » Kindred Souls » Доска объявлений » Нужные


    Нужные

    Сообщений 1 страница 6 из 6

    1

    ***

    0

    2

    Тест

    0

    3

    [html]
    <div class="hover-swap">
      <div class="hover-swap__media">
        <img class="img img--base" src="https://upforme.ru/uploads/001c/0a/76/4/613957.jpg">
        <img class="img img--hover" src="https://upforme.ru/uploads/001c/0a/76/4/729374.jpg">
      </div>

      <div class="hover-swap__caption" aria-hidden="true">
        <p>С Новым годом и Рождеством, Ривер.</p>

        <p>
          Пусть этот год будет к тебе чуть мягче, чем предыдущий: дел — меньше, сна — больше,
          а поводов держать челюсти сжатыми — как можно реже. Пусть интуиция ведёт туда,
          куда нужно, а не туда, куда безопасно, и рядом остаются те, ради кого вообще стоит держаться.
        </p>

        <p>
          И пусть мир в новом году иногда по привычке недооценивает тебя — так ему проще.
          Мы-то знаем, что твоя сила измеряется не сантиметрами роста, а тем, какая тень остаётся за спиной.
        </p>

        <p>
          А подарки ты уже можешь найти в своей карточке:
        </p>

        <div class="hover-swap__gifts">
          <div class="hover-swap__gift">
            <div class="hover-swap__gift-title">Иконка:</div>
            <img class="hover-swap__gift-img" src="https://upforme.ru/uploads/001c/82/f2/4/867040.png">
          </div>

          <div class="hover-swap__gift">
            <div class="hover-swap__gift-title">Плашка:</div>
            <img class="hover-swap__gift-img" src="https://upforme.ru/uploads/001c/82/f2/4/542544.png">
          </div>
        </div>
      </div>
    </div>

    <style>
    .hover-swap {
      width: 300px;
      display: inline-block;
      line-height: 0;
    }

    .hover-swap__media {
      position: relative;
    }

    .hover-swap .img {
      display: block;
      width: 100%;
      height: auto;
    }

    .hover-swap .img--hover {
      position: absolute;
      inset: 0;
      opacity: 0;
      transition: opacity .25s ease;
    }

    .hover-swap__caption {
      line-height: 1.4;
      margin-top: 12px;
      opacity: 0;
      transform: translateY(12px);
      filter: blur(4px);

      transition:
        opacity .45s ease,
        transform .45s ease,
        filter .45s ease;
      transition-delay: .35s;
    }

    .hover-swap__caption p {
      margin: 0 0 8px 0;
    }

    .hover-swap__caption p:last-child {
      margin-bottom: 0;
    }

    .hover-swap__gifts {
      margin-top: 10px;
      line-height: 1.4;
      text-align: center;
    }

    .hover-swap__gift {
      display: block;
      margin: 12px 0 0 0;
    }

    .hover-swap__gift:first-child {
      margin-top: 0;
    }

    .hover-swap__gift-title {
      font-weight: 700;
      margin-bottom: 6px;
    }

    .hover-swap__gift-img {
      display: block;
      margin: 0 auto;
      max-width: 100%;
      height: auto;
    }

    .hover-swap:hover .img--hover {
      opacity: 1;
    }

    .hover-swap:hover .hover-swap__caption {
      opacity: 1;
      transform: translateY(0);
      filter: blur(0);
    }
    </style>
    [/html]

    0

    4

    Вообще, мне самому не часто приходится заниматься вёрсткой, хотя дело я от части люблю. Но именно из-за того, что делаю я это нечасто, мне периодически приходится обращаться к документации, чтобы вспомнить некоторые свойства и значения современного CSS.
    В конечном итоге я решил накидать шпаргалку для себя, которая постепенно выросла в небольшую статью, представленную ниже. В ней описаны несколько действительно приятных современных возможностей CSS с примерами того, как стили писались в прошлом и тем, как это можно делать сейчас.  Сама идея представленных возможностей достаточна простая: меньше дублирования, меньше костылей, больше компонентного подхода.

    "CSS Nesting" и "&"

    Что это такое:
    CSS Nesting позволяет вкладывать один селектор в другой прямо в обычном CSS. Браузер при этом сам понимает такую запись, что делает стили короче и более читабельными. Символ "&" нужен, когда вложенный селектор должен "прицепиться" к своему родителю. Например, для :hover.

    Как было раньше:

    Код:
    <div class="card">
      <h2 class="card__title">Заголовок</h2>
      <p class="card__text">Немного текста внутри карточки.</p>
      <a class="card__link" href="#">Открыть</a>
    </div>
    
    <style>
      .card {
        padding: 16px;
        border: 1px solid #ccc;
        border-radius: 12px;
      }
    
      .card:hover {
        border-color: #333;
      }
    
      .card__title {
        margin: 0 0 8px;
      }
    
      .card__text {
        margin: 0 0 12px;
        color: #666;
      }
    
      .card__link {
        color: royalblue;
        text-decoration: none;
      }
    
      .card__link:hover {
        text-decoration: underline;
      }
    </style>
    

    Как можно сейчас:

    Код:
    <div class="card">
      <h2 class="card__title">Заголовок</h2>
      <p class="card__text">Немного текста внутри карточки.</p>
      <a class="card__link" href="#">Открыть</a>
    </div>
    
    <style>
      .card {
        padding: 16px;
        border: 1px solid #ccc;
        border-radius: 12px;
    
        &:hover {
          border-color: #333;
        }
    
        .card__title {
          margin: 0 0 8px;
        }
    
        .card__text {
          margin: 0 0 12px;
          color: #666;
        }
    
        .card__link {
          color: royalblue;
          text-decoration: none;
    
          &:hover {
            text-decoration: underline;
          }
        }
      }
    </style>
    

    Результат:
    [html]
    <div class="card">
      <h2 class="card__title">Заголовок</h2>
      <p class="card__text">Немного текста внутри карточки.</p>
      <a class="card__link" href="#">Открыть</a>
    </div>

    <style>
      .card {
        padding: 16px;
        border: 1px solid #ccc;
        border-radius: 12px;

        &:hover {
          border-color: #333;
        }

        .card__title {
          margin: 0 0 8px;
        }

        .card__text {
          margin: 0 0 12px;
          color: #666;
        }

        .card__link {
          color: royalblue;
          text-decoration: none;

          &:hover {
            text-decoration: underline;
          }
        }
      }
    </style>
    [/html]

    Где особенно удобно: карточки, меню, формы, состояния вроде :hover, :focus, :disabled.


    @layer

    Что это такое:
    @layer даёт нам явные слои каскада. Проще говоря, можно заранее решить, что reset слабее components, а utilities сильнее их обоих. Это полезно, когда проект растёт и обычная борьба специфичности начинает раздражать. У слоя приоритет определяется не тем, как сложно и хитро мы написали селектор, а порядком слоёв.

    Как было раньше:

    Код:
    <style>
      .button {
        background: gray;
        color: white;
      }
    
      .page .content .button-primary {
        background: seagreen;
      }
    </style>
    
    <button class="button button-primary">Кнопка</button>
    

    Такой подход легко приводил нас к ситуациям, когда мы начинали писать всё более сложные и запутанные селекторы.

    А вот как можно сейчас:

    Код:
    <style>
      @layer reset, components, utilities;
    
      @layer reset {
        button {
          font: inherit;
        }
      }
    
      @layer components {
        .button {
          padding: 10px 16px;
          border: 0;
          border-radius: 10px;
          background: gray;
          color: white;
        }
      }
    
      @layer utilities {
        .button-primary {
          background: seagreen;
        }
      }
    </style>
    <button class="button button-primary">Кнопка</button>
    

    [html]
    <style>
      @layer reset, components, utilities;

      @layer reset {
        button {
          font: inherit;
        }
      }

      @layer components {
        .button {
          padding: 10px 16px;
          border: 0;
          border-radius: 10px;
          background: gray;
          color: white;
        }
      }

      @layer utilities {
        .button-primary {
          background: seagreen;
        }
      }
    </style>
    <button class="button button-primary">Кнопка</button>
    [/html]

    На первый взгляд может показаться, что так сложнее и больше кода. Но всё меняется, когда разметка становится большой, а селекторов для неё много. Идея @layer простая: сначала объявили порядок слоёв, потом спокойно раскладываем стили по смыслу.


    :is() и :where()

    Что это такое:
    Обе функции помогают группировать селекторы. При этом у них есть важная разница: :is() берёт специфичность самого "сильного" селектора внутри, а :where() всегда имеет нулевую специфичность. Поэтому :is() удобно использовать для сокращения записи, а :where() — для базовых стилей, которые потом должно быть легко переопределить.

    Как было раньше:

    Код:
    <style>
      .content h1,
      .content h2,
      .content h3 {
        color: #222;
        line-height: 1.2;
      }
    </style>
    <section class="content">
      <h1>Заголовок</h1>
      <h2>Подзаголовок</h2>
      <h3>Ещё один подзаголовок</h3>
    </section>

    А вот как можно сейчас с :is()

    Код:
    <style>
      .content :is(h1, h2, h3) {
        color: #222;
        line-height: 1.2;
      }
    </style>
    <section class="content">
      <h1>Заголовок</h1>
      <h2>Подзаголовок</h2>
      <h3>Ещё один подзаголовок</h3>
    </section>
    

    А так можно с :where()

    Код:
    <style>
      :where(.site-footer a) {
        color: tomato;
      }
    
      footer a {
        color: steelblue;
      }
    </style>
    <footer class="site-footer">
      <a href="#">Ссылка в футере</a>
    </footer>
    

    [html]
    <style>
      :where(.site-footer a) {
        color: tomato;
      }

      footer a {
        color: steelblue;
      }
    </style>
    <footer class="site-footer">
      <a href="#">Ссылка в футере</a>
    </footer>
    [/html]

    Здесь цвет из "footer a" легко победит правило с :where(), потому что у :where() нулевая специфичность, что удобно для базовых стилей.

    :has()

    Что это такое:
    :has() является селектором отношений, который позволяет выбрать элемент, в случае если внутри него или рядом с ним есть что-то подходящее. Этот селектор можно стилизовать форму, если внутри есть невалидный input, карточку - если в ней есть картинка, заголовок - если после него идёт подзаголовок.

    Как было раньше:

    Обычно приходилось либо вешать класс через JavaScript, либо добавлять лишнюю служебную разметку.

    Код:
    <style>
      .form-error {
        border: 2px solid crimson;
      }
    </style>
    <form class="form form-error">
      <label>
        Email
        <input type="email" value="не email">
      </label>
    </form>
    

    Как можно сейчас:

    Код:
    <style>
      .form {
        padding: 16px;
        border: 2px solid #ccc;
        border-radius: 12px;
      }
    
      .form:has(input:invalid) {
        border-color: crimson;
        background: #fff5f5;
      }
    </style>
    <form class="form">
      <label>
        Email
        <input type="email" value="не email" required>
      </label>
    </form>
    

    [html]
    <style>
      .form {
        padding: 16px;
        border: 2px solid #ccc;
        border-radius: 12px;
      }

      .form:has(input:invalid) {
        border-color: crimson;
        background: #fff5f5;
      }
    </style>
    <form class="form">
      <label>
        Email
        <input type="email" value="не email" required>
      </label>
    </form>
    [/html]

    Ещё один пример: выделение карточки, только если в ней есть элемент с классом (.badge):

    Код:
    <style>
      .product-card {
        padding: 16px;
        border: 1px solid #ccc;
      }
    
      .product-card:has(.badge) {
        border-color: orange;
      }
    </style>
    <article class="product-card">
      <span class="badge">new</span>
      <h2>Наушники</h2>
    </article>
    

    [html]
    <style>
      .product-card {
        padding: 16px;
        border: 1px solid #ccc;
      }

      .product-card:has(.badge) {
        border-color: orange;
      }
    </style>
    <article class="product-card">
      <span class="badge">new</span>
    </article>
    [/html]

    @container

    Что это такое:
    Раньше мы почти всегда пользовались только viewport’ом: "если экран меньше 768px, делай так". Но компонент может жить и в широкой колонке, и в узком сайдбаре на одном и том же экране. @container позволяет менять стили по размеру контейнера, а не окна браузера. Для этого контейнер нужно явно объявить через container-type или shorthand container.

    Как было раньше: только media query

    Код:
    <style>
      .card {
        padding: 16px;
        font-size: 14px;
      }
    
      @media (min-width: 900px) {
        .card {
          font-size: 18px;
        }
      }
    </style>
    <div class="layout">
      <article class="card">
        <h2>Новость</h2>
        <p>Карточка меняется только от размера окна.</p>
      </article>
    </div>
    

    Тут возникала проблема: если карточка стоит в узкой колонке на широком экране, она всё равно получит "широкие" стили.

    А вот как можно сейчас:

    Код:
    <style>
      .sidebar {
        container-type: inline-size;
        width: 320px;
      }
    
      .card {
        padding: 16px;
        font-size: 14px;
        border: 1px solid #ccc;
        border-radius: 12px;
      }
    
      @container (width > 400px) {
        .card {
          font-size: 18px;
          display: grid;
          gap: 12px;
        }
      }
    </style>
    <div class="sidebar">
      <article class="card">
        <h2>Новость</h2>
        <p>Теперь карточка смотрит на ширину родителя.</p>
      </article>
    </div>
    

    [html]
    <style>
      .sidebar {
        container-type: inline-size;
        width: 320px;
      }

      .card {
        padding: 16px;
        font-size: 14px;
        border: 1px solid #ccc;
        border-radius: 12px;
      }

      @container (width > 400px) {
        .card {
          font-size: 18px;
          display: grid;
          gap: 12px;
        }
      }
    </style>
    <div class="sidebar">
      <article class="card">
        <h2>Новость</h2>
        <p>Теперь карточка смотрит на ширину родителя.</p>
      </article>
    </div>
    [/html]

    А shorthand выглядит так:

    Код:
    .sidebar {
      container: sidebar / inline-size;
    }
    

    dvh, lvh, svh

    Что это такое:
    Классическая проблема мобильной вёрстки - 100vh. На телефонах видимая область может меняться из-за адресной строки и интерфейса браузера. Новые viewport-единицы помогают описывать это точнее: svh — маленький viewport, lvh — большой, dvh — динамический текущий. Документация отдельно отмечает, что vh эквивалентен lvh, то есть ориентируется на большой viewport.

    Как было раньше:

    Код:
    <style>
      .hero {
        height: 100vh;
        display: grid;
        place-items: center;
        background: peachpuff;
      }
    </style>
    <section class="hero">
      <h1>Привет</h1>
    </section>

    На мобильных такой блок иногда оказывается выше реально видимой области.

    Как можно сейчас:

    Код:
    <style>
      .hero {
        min-height: 100dvh;
        display: grid;
        place-items: center;
        background: peachpuff;
      }
    </style>
    <section class="hero">
      <h1>Привет</h1>
    </section>
    

    Полезная шпаргалка:
    100svh - безопасный минимум, когда весь интерфейс браузера виден;
    100lvh - максимум;
    100dvh - текущее динамическое значение.

    https://www.mediaevent.de/wp-content/uploads/2024/04/dynamic-vh.svg

    aspect-ratio

    Что это такое:
    aspect-ratio задаёт желаемое соотношение ширины к высоте, и браузер сам поддерживает пропорции элемента при расчёте размеров. Но есть нюанс: хотя бы одна из сторон должна быть автоматической, иначе свойство не влияет на размеры.

    Как было раньше:

    Код:
    <style>
      .video {
        position: relative;
        padding-top: 56.25%;
      }
    
      .video iframe {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        border: 0;
      }
    </style>
    <div class="video">
      <iframe src="about:blank" title="Видео"></iframe>
    </div>
    

    Как можно сейчас:

    Код:
    <style>
      .video {
        aspect-ratio: 16 / 9;
      }
    
      .video iframe {
        width: 100%;
        height: 100%;
        border: 0;
      }
    </style>
    <div class="video">
      <iframe src="about:blank" title="Видео"></iframe>
    </div>
    

    [html]
    <style>
      .video {
        aspect-ratio: 16 / 9;
      }

      .video iframe {
        width: 100%;
        height: 100%;
        border: 0;
      }
    </style>
    <div class="video">
      <iframe src="about:blank" title="Видео"></iframe>
    </div>
    [/html]

    Или для карточек-превью:

    Код:
    <style>
      .thumb {
        width: 100%;
        aspect-ratio: 1 / 1;
        background: linear-gradient(135deg, #89f7fe, #66a6ff);
        border-radius: 16px;
      }
    </style>
    <div class="thumb"></div>
    

    [html]
    <style>
      .thumb {
        width: 100%;
        aspect-ratio: 1 / 1;
        background: linear-gradient(135deg, #89f7fe, #66a6ff);
        border-radius: 16px;
      }
    </style>
    <div class="thumb"></div>
    [/html]

    Просто и читаемо.

    color-scheme

    Что это такое:
    color-scheme говорит браузеру, в каких схемах элемент или страница должны отображаться: light, dark или оба варианта.

    Как было раньше:

    Код:
    <style>
      .panel {
        background: #111;
        color: #eee;
        padding: 16px;
      }
    </style>
    <form class="panel">
      <input type="text" placeholder="Ваше имя">
      <textarea placeholder="Комментарий"></textarea>
    </form>
    

    Проблема была достаточно явной: отдельные элементы могли выглядеть не очень согласованно.

    Как можно сейчас:

    Код:
    <style>
      .panel {
        color-scheme: dark;
        background: #111;
        color: #eee;
        padding: 16px;
        border-radius: 12px;
      }
    
      .panel input,
      .panel textarea {
        width: 100%;
        margin-top: 10px;
      }
    </style>
    <form class="panel">
      <input type="text" placeholder="Ваше имя">
      <textarea placeholder="Комментарий"></textarea>
    </form>
    

    Или если хотите поддерживать обе темы:

    Код:
    <style>
      .app {
        color-scheme: light dark;
        background: #fff;
        color: #111;
        padding: 16px;
      }
    
      @media (prefers-color-scheme: dark) {
        .app {
          background: #111;
          color: #eee;
        }
      }
    </style>
    <div class="app">
      <h2>Настройки</h2>
      <input type="text" placeholder="Поиск">
    </div>
    

    [html]
    <style>
      .app {
        color-scheme: light dark;
        background: #fff;
        color: #111;
        padding: 16px;
      }

      @media (prefers-color-scheme: dark) {
        .app {
          background: #111;
          color: #eee;
        }
      }
    </style>
    <div class="app">
      <h2>Настройки</h2>
      <input type="text" placeholder="Поиск">
    </div>
    [/html]

    Итог:
    Nesting и & позволяют писать меньше повторов и делают стили чище.
    @layer позволяет облегчить работу со специфичностью.
    :is() и :where() дают нам более короткие и аккуратные селекторы, плюс контроль над специфичностью.
    :has() позволяет наконец-то можно смотреть внутрь элемента и стилизовать родителя.
    @container делает компоненты по-настоящему самостоятельными.
    dvh/lvh/svh облегчают работу с мобильной высотой.
    aspect-ratio упрощают работу с соотношением сторон.
    color-scheme - аккуратная интеграция светлой/тёмной темы с браузерным UI.

    0

    5

    Вообще, мне самому нечасто приходится заниматься вёрсткой, хотя само это дело мне скорее нравится. Но именно из-за того, что возвращаюсь к нему не так уж регулярно, время от времени приходится снова заглядывать в документацию, чтобы освежить в памяти свойства и возможности современного CSS.

    В какой-то момент я решил собрать для себя небольшую шпаргалку, а потом она постепенно выросла в короткую статью. Ниже — несколько действительно приятных современных возможностей CSS с короткой теорией, примерами того, как это обычно делали раньше, и тем, как можно писать сейчас. Сама идея у большинства этих возможностей довольно простая: меньше дублирования, меньше костылей, больше удобства и компонентного подхода.

    CSS Nesting и &

    Что это такое:
    CSS Nesting позволяет вкладывать один селектор в другой прямо в обычном CSS. Браузер сам понимает такую запись, поэтому стили становятся компактнее и читаются заметно легче. Символ & нужен, когда вложенный селектор должен "прицепиться" к родителю без пробела. Например, для :hover, :focus, .is-active и похожих состояний.

    Как было раньше:

    Код:
    <div class="promo-card">
      <div class="promo-card__badge">new</div>
      <div class="promo-card__title">Новая коллекция</div>
      <div class="promo-card__text">Лёгкие вещи на тёплую погоду.</div>
      <a class="promo-card__button" href="#">Смотреть</a>
    </div>
    
    <style>
      .promo-card {
        max-width: 320px;
        padding: 18px;
        border: 1px solid #d9d9d9;
        border-radius: 16px;
        background: #ffffff;
      }
    
      .promo-card:hover {
        border-color: #8c7ae6;
        box-shadow: 0 10px 24px rgba(0, 0, 0, 0.08);
      }
    
      .promo-card__badge {
        display: inline-block;
        margin-bottom: 12px;
        padding: 4px 10px;
        border-radius: 999px;
        background: #f3edff;
        color: #6c43d7;
        font-size: 12px;
      }
    
      .promo-card__title {
        margin-bottom: 8px;
        font-size: 20px;
        font-weight: 700;
      }
    
      .promo-card__text {
        margin-bottom: 14px;
        color: #666;
      }
    
      .promo-card__button {
        display: inline-block;
        padding: 10px 14px;
        border-radius: 10px;
        background: #8c7ae6;
        color: #fff;
        text-decoration: none;
      }
    
      .promo-card__button:hover {
        background: #735fe0;
      }
    </style>
    

    Как можно сейчас:

    Код:
    <div class="promo-card">
      <div class="promo-card__badge">new</div>
      <div class="promo-card__title">Новая коллекция</div>
      <div class="promo-card__text">Лёгкие вещи на тёплую погоду.</div>
      <a class="promo-card__button" href="#">Смотреть</a>
    </div>
    
    <style>
      .promo-card {
        max-width: 320px;
        padding: 18px;
        border: 1px solid #d9d9d9;
        border-radius: 16px;
        background: #ffffff;
    
        &:hover {
          border-color: #8c7ae6;
          box-shadow: 0 10px 24px rgba(0, 0, 0, 0.08);
        }
    
        .promo-card__badge {
          display: inline-block;
          margin-bottom: 12px;
          padding: 4px 10px;
          border-radius: 999px;
          background: #f3edff;
          color: #6c43d7;
          font-size: 12px;
        }
    
        .promo-card__title {
          margin-bottom: 8px;
          font-size: 20px;
          font-weight: 700;
        }
    
        .promo-card__text {
          margin-bottom: 14px;
          color: #666;
        }
    
        .promo-card__button {
          display: inline-block;
          padding: 10px 14px;
          border-radius: 10px;
          background: #8c7ae6;
          color: #fff;
          text-decoration: none;
    
          &:hover {
            background: #735fe0;
          }
        }
      }
    </style>
    

    Результат:

    [html]
    <div class="demo demo-nesting">
      <div class="promo-card">
        <div class="promo-card__badge">new</div>
        <div class="promo-card__title">Новая коллекция</div>
        <div class="promo-card__text">Лёгкие вещи на тёплую погоду.</div>
        <a class="promo-card__button" href="#">Смотреть</a>
      </div>
    </div>

    <style>
      .demo-nesting {
        padding: 20px;
        border: 1px solid #e6e6e6;
        border-radius: 18px;
        background: linear-gradient(180deg, #fcfcff, #f6f3ff);
      }

      .promo-card {
        max-width: 320px;
        padding: 18px;
        border: 1px solid #d9d9d9;
        border-radius: 16px;
        background: #ffffff;
        font-family: Arial, sans-serif;

        &:hover {
          border-color: #8c7ae6;
          box-shadow: 0 10px 24px rgba(0, 0, 0, 0.08);
        }

        .promo-card__badge {
          display: inline-block;
          margin-bottom: 12px;
          padding: 4px 10px;
          border-radius: 999px;
          background: #f3edff;
          color: #6c43d7;
          font-size: 12px;
          text-transform: uppercase;
          letter-spacing: 0.08em;
        }

        .promo-card__title {
          margin-bottom: 8px;
          font-size: 20px;
          font-weight: 700;
          color: #1f1f1f;
        }

        .promo-card__text {
          margin-bottom: 14px;
          color: #666;
          line-height: 1.45;
        }

        .promo-card__button {
          display: inline-block;
          padding: 10px 14px;
          border-radius: 10px;
          background: #8c7ae6;
          color: #fff;
          text-decoration: none;

          &:hover {
            background: #735fe0;
          }
        }
      }
    </style>
    [/html]

    Где особенно удобно: карточки, меню, формы, состояния вроде :hover, :focus, :disabled, а также BEM-структуры.


    @layer

    Что это такое:
    @layer даёт явные слои каскада. Проще говоря, можно заранее решить, что reset слабее components, а utilities сильнее их обоих. Это удобно, когда проект становится больше, а обычная борьба со специфичностью начинает раздражать.

    Как было раньше:

    Код:
    <style>
      .tag {
        display: inline-block;
        padding: 8px 12px;
        border-radius: 999px;
        background: #e5e5e5;
        color: #333;
      }
    
      .page .content .tag-green {
        background: #1f9d55;
        color: #fff;
      }
    </style>
    
    <div class="tag tag-green">Опубликовано</div>
    

    Такой подход легко приводил к тому, что ради переопределения приходилось писать всё более тяжёлые селекторы.

    Как можно сейчас:

    Код:
    <style>
      @layer reset, components, utilities;
    
      @layer reset {
        * {
          box-sizing: border-box;
        }
      }
    
      @layer components {
        .tag {
          display: inline-block;
          padding: 8px 12px;
          border-radius: 999px;
          background: #e5e5e5;
          color: #333;
          font-family: Arial, sans-serif;
        }
      }
    
      @layer utilities {
        .tag-green {
          background: #1f9d55;
          color: #fff;
        }
      }
    </style>
    
    <div class="tag tag-green">Опубликовано</div>
    

    Результат:

    [html]
    <div class="demo demo-layer">
      <div class="tag">Черновик</div>
      <div class="tag tag-green">Опубликовано</div>
      <div class="tag tag-red">Ошибка</div>
    </div>

    <style>
      @layer reset, components, utilities;

      @layer reset {
        * {
          box-sizing: border-box;
        }
      }

      @layer components {
        .demo-layer {
          padding: 20px;
          border: 1px solid #e6e6e6;
          border-radius: 18px;
          background: #fafafa;
          font-family: Arial, sans-serif;
        }

        .tag {
          display: inline-block;
          margin-right: 8px;
          margin-bottom: 8px;
          padding: 8px 12px;
          border-radius: 999px;
          background: #e5e5e5;
          color: #333;
          font-size: 14px;
        }
      }

      @layer utilities {
        .tag-green {
          background: #1f9d55;
          color: #fff;
        }

        .tag-red {
          background: #d64545;
          color: #fff;
        }
      }
    </style>
    [/html]

    На первый взгляд может показаться, что кода стало даже больше. Но на больших проектах @layer сильно упрощает жизнь: сначала объявили порядок слоёв, а потом спокойно раскладываем стили по смыслу.


    :is() и :where()

    Что это такое:
    Обе функции помогают группировать селекторы. Но есть важная разница: :is() берёт специфичность самого "сильного" селектора внутри, а :where() всегда имеет нулевую специфичность. Поэтому :is() удобно использовать для сокращения записи, а :where() — для базовых стилей, которые потом должно быть легко переопределить.

    Как было раньше:

    Код:
    <style>
      .toolbar a,
      .toolbar button,
      .toolbar input {
        padding: 10px 12px;
        border: 1px solid #d8d8d8;
        border-radius: 10px;
        background: #fff;
        color: #333;
        font: inherit;
      }
    </style>
    
    <div class="toolbar">
      <a href="#">Ссылка</a>
      <button type="button">Кнопка</button>
      <input type="text" value="Поле">
    </div>
    

    Как можно сейчас с :is()

    Код:
    <style>
      .toolbar :is(a, button, input) {
        padding: 10px 12px;
        border: 1px solid #d8d8d8;
        border-radius: 10px;
        background: #fff;
        color: #333;
        font: inherit;
      }
    </style>
    
    <div class="toolbar">
      <a href="#">Ссылка</a>
      <button type="button">Кнопка</button>
      <input type="text" value="Поле">
    </div>
    

    А так можно с :where()

    Код:
    <style>
      :where(.article-card a) {
        color: #6c43d7;
        text-decoration: none;
      }
    
      .article-card--warning a {
        color: #c0392b;
      }
    </style>
    
    <div class="article-card">Обычная <a href="#">ссылка</a></div>
    <div class="article-card article-card--warning">Важная <a href="#">ссылка</a></div>
    

    Результат:

    [html]
    <div class="demo demo-iswhere">
      <div class="toolbar">
        <a href="#">Ссылка</a>
        <button type="button">Кнопка</button>
        <input type="text" value="Поле">
      </div>

      <div class="article-card">
        Базовая карточка со <a href="#">ссылкой</a>
      </div>

      <div class="article-card article-card--warning">
        Важная карточка со <a href="#">ссылкой</a>
      </div>
    </div>

    <style>
      .demo-iswhere {
        padding: 20px;
        border: 1px solid #e6e6e6;
        border-radius: 18px;
        background: #fcfcfc;
        font-family: Arial, sans-serif;
      }

      .toolbar {
        display: flex;
        flex-wrap: wrap;
        gap: 10px;
        margin-bottom: 18px;
      }

      .toolbar :is(a, button, input) {
        padding: 10px 12px;
        border: 1px solid #d8d8d8;
        border-radius: 10px;
        background: #fff;
        color: #333;
        font: inherit;
        text-decoration: none;
      }

      .article-card {
        margin-top: 10px;
        padding: 14px;
        border-radius: 14px;
        background: #f3f3f3;
        color: #333;
      }

      :where(.article-card a) {
        color: #6c43d7;
        text-decoration: none;
        font-weight: 700;
      }

      .article-card--warning {
        background: #fff2f0;
        border: 1px solid #ffd0ca;
      }

      .article-card--warning a {
        color: #c0392b;
      }
    </style>
    [/html]

    Здесь :is() сокращает запись, а :where() удобно использовать как мягкую базу, которую потом легко переопределить.


    :has()

    Что это такое:
    :has() — это селектор отношений. Он позволяет выбрать элемент, если внутри него или рядом с ним есть что-то подходящее. Проще говоря, с его помощью можно стилизовать родителя в зависимости от содержимого.

    Как было раньше:

    Обычно приходилось либо добавлять класс через JavaScript, либо вручную прописывать служебные классы в разметке.

    Код:
    <style>
      .option-selected {
        border-color: #4f46e5;
        background: #eef0ff;
      }
    </style>
    
    <label class="option option-selected">
      <input type="checkbox" checked>
      Тёмная тема
    </label>
    

    Как можно сейчас:

    Код:
    <style>
      .option {
        display: flex;
        align-items: center;
        gap: 10px;
        padding: 14px;
        border: 2px solid #d9d9d9;
        border-radius: 14px;
        background: #fff;
      }
    
      .option:has(input:checked) {
        border-color: #4f46e5;
        background: #eef0ff;
      }
    </style>
    
    <label class="option">
      <input type="checkbox" checked>
      Тёмная тема
    </label>
    

    Результат:

    [html]
    <div class="demo demo-has">
      <label class="option">
        <input type="checkbox" checked>
        <span>Тёмная тема</span>
      </label>

      <label class="option">
        <input type="checkbox">
        <span>Системные уведомления</span>
      </label>
    </div>

    <style>
      .demo-has {
        padding: 20px;
        border: 1px solid #e6e6e6;
        border-radius: 18px;
        background: #fafafe;
        font-family: Arial, sans-serif;
      }

      .option {
        display: flex;
        align-items: center;
        gap: 10px;
        margin-bottom: 10px;
        padding: 14px;
        border: 2px solid #d9d9d9;
        border-radius: 14px;
        background: #fff;
        transition: 0.2s ease;
      }

      .option:last-child {
        margin-bottom: 0;
      }

      .option:has(input:checked) {
        border-color: #4f46e5;
        background: #eef0ff;
        box-shadow: 0 6px 16px rgba(79, 70, 229, 0.12);
      }
    </style>
    [/html]

    Это как раз тот случай, когда раньше очень часто приходилось лезть в JavaScript ради сравнительно простой визуальной реакции интерфейса.


    @container

    Что это такое:
    Раньше мы почти всегда ориентировались только на viewport: "если экран меньше такой-то ширины, делай так". Но один и тот же компонент может стоять и в широкой колонке, и в узком сайдбаре. @container позволяет менять стили по размеру контейнера, а не окна браузера.

    Как было раньше:

    Код:
    <style>
      .news-card {
        padding: 16px;
        border: 1px solid #d9d9d9;
        border-radius: 14px;
      }
    
      @media (min-width: 900px) {
        .news-card {
          display: flex;
          gap: 14px;
        }
      }
    </style>
    
    <div class="news-card">
      <div class="news-card__image"></div>
      <div class="news-card__body">Карточка зависит только от размера окна.</div>
    </div>
    

    Проблема в том, что если такая карточка стоит в узкой колонке на широком экране, она всё равно получит "широкие" стили.

    Как можно сейчас:

    Код:
    <style>
      .column {
        container-type: inline-size;
      }
    
      @container (width > 420px) {
        .news-card {
          display: flex;
          gap: 14px;
          align-items: center;
        }
    
        .news-card__image {
          width: 120px;
          flex-shrink: 0;
        }
      }
    </style>
    

    Результат:

    [html]
    <div class="demo demo-container">
      <div class="column narrow">
        <div class="news-card">
          <div class="news-card__image"></div>
          <div class="news-card__body">
            <div class="news-card__title">Узкий контейнер</div>
            <div class="news-card__text">Карточка остаётся вертикальной, потому что ей тесно.</div>
          </div>
        </div>
      </div>

      <div class="column wide">
        <div class="news-card">
          <div class="news-card__image"></div>
          <div class="news-card__body">
            <div class="news-card__title">Широкий контейнер</div>
            <div class="news-card__text">А здесь тот же компонент уже перестраивается в строку.</div>
          </div>
        </div>
      </div>
    </div>

    <style>
      .demo-container {
        display: grid;
        gap: 16px;
        padding: 20px;
        border: 1px solid #e6e6e6;
        border-radius: 18px;
        background: #fcfcfc;
        font-family: Arial, sans-serif;
      }

      .column {
        container-type: inline-size;
        padding: 12px;
        border-radius: 16px;
        background: #f5f5f5;
      }

      .column.narrow {
        max-width: 280px;
      }

      .column.wide {
        max-width: 560px;
      }

      .news-card {
        padding: 14px;
        border: 1px solid #d9d9d9;
        border-radius: 14px;
        background: #fff;
      }

      .news-card__image {
        width: 100%;
        height: 90px;
        margin-bottom: 12px;
        border-radius: 12px;
        background: linear-gradient(135deg, #7f7fd5, #86a8e7, #91eae4);
      }

      .news-card__title {
        margin-bottom: 8px;
        font-size: 18px;
        font-weight: 700;
        color: #222;
      }

      .news-card__text {
        color: #666;
        line-height: 1.45;
      }

      @container (width > 420px) {
        .news-card {
          display: flex;
          gap: 14px;
          align-items: center;
        }

        .news-card__image {
          width: 120px;
          height: 90px;
          margin-bottom: 0;
          flex-shrink: 0;
        }
      }
    </style>
    [/html]

    Именно поэтому @container особенно хорош для компонентного подхода: один и тот же блок ведёт себя по-разному в зависимости от того, сколько места ему реально дали.


    dvh, lvh, svh

    Что это такое:
    Классическая проблема мобильной вёрстки — 100vh. На телефонах видимая область может меняться из-за адресной строки и элементов интерфейса браузера. Новые единицы viewport помогают описывать это точнее:
    [list]
    [*]svh — маленький viewport;
    [*]lvh — большой viewport;
    [*]dvh — динамический текущий viewport.
    [/list]

    Как было раньше:

    Код:
    <style>
      .welcome-screen {
        height: 100vh;
        display: grid;
        place-items: center;
      }
    </style>
    
    <div class="welcome-screen">Привет</div>
    

    На мобильных это иногда приводило к тому, что блок оказывался выше реально видимой области.

    Как можно сейчас:

    Код:
    <style>
      .welcome-screen {
        min-height: 100dvh;
        display: grid;
        place-items: center;
      }
    </style>
    
    <div class="welcome-screen">Привет</div>
    

    Результат:

    [html]
    <div class="demo demo-dvh">
      <div class="welcome-screen">
        <div class="welcome-screen__card">
          <div class="welcome-screen__title">Полноэкранный блок</div>
          <div class="welcome-screen__text">На телефонах для таких секций чаще полезнее использовать 100dvh, чем старый 100vh.</div>
        </div>
      </div>
    </div>

    <style>
      .demo-dvh {
        padding: 20px;
        border: 1px solid #e6e6e6;
        border-radius: 18px;
        background: #fcfcff;
        font-family: Arial, sans-serif;
      }

      .welcome-screen {
        min-height: 45dvh;
        display: grid;
        place-items: center;
        border-radius: 16px;
        background: linear-gradient(135deg, #ffecd2, #fcb69f);
      }

      .welcome-screen__card {
        max-width: 320px;
        padding: 18px;
        border-radius: 16px;
        background: rgba(255, 255, 255, 0.85);
        text-align: center;
      }

      .welcome-screen__title {
        margin-bottom: 8px;
        font-size: 20px;
        font-weight: 700;
        color: #222;
      }

      .welcome-screen__text {
        color: #555;
        line-height: 1.45;
      }
    </style>
    [/html]

    Полезная шпаргалка:
    [list]
    [*]100svh — безопасный минимум, когда интерфейс браузера виден;
    [*]100lvh — максимум;
    [*]100dvh — текущее динамическое значение.
    [/list]

    На десктопе разница может быть почти незаметной, а вот на мобильных это уже куда полезнее.


    aspect-ratio

    Что это такое:
    aspect-ratio задаёт желаемое соотношение ширины к высоте, а браузер сам поддерживает пропорции элемента при расчёте размеров. Очень удобная вещь для превью, карточек, баннеров, видео и галерей.

    Как было раньше:

    Код:
    <style>
      .poster {
        position: relative;
        padding-top: 100%;
        border-radius: 16px;
        overflow: hidden;
      }
    
      .poster__inner {
        position: absolute;
        inset: 0;
      }
    </style>
    
    <div class="poster">
      <div class="poster__inner"></div>
    </div>
    

    Как можно сейчас:

    Код:
    <style>
      .poster {
        aspect-ratio: 1 / 1;
        border-radius: 16px;
      }
    </style>
    
    <div class="poster"></div>
    

    Результат:

    [html]
    <div class="demo demo-ratio">
      <div class="gallery">
        <div class="poster poster--square">1:1</div>
        <div class="poster poster--video">16:9</div>
        <div class="poster poster--book">3:4</div>
      </div>
    </div>

    <style>
      .demo-ratio {
        padding: 20px;
        border: 1px solid #e6e6e6;
        border-radius: 18px;
        background: #fafafa;
        font-family: Arial, sans-serif;
      }

      .gallery {
        display: grid;
        gap: 14px;
      }

      .poster {
        display: grid;
        place-items: center;
        border-radius: 16px;
        color: #fff;
        font-size: 22px;
        font-weight: 700;
      }

      .poster--square {
        aspect-ratio: 1 / 1;
        background: linear-gradient(135deg, #43cea2, #185a9d);
      }

      .poster--video {
        aspect-ratio: 16 / 9;
        background: linear-gradient(135deg, #f7971e, #ffd200);
      }

      .poster--book {
        aspect-ratio: 3 / 4;
        background: linear-gradient(135deg, #834d9b, #d04ed6);
      }
    </style>
    [/html]

    Такая запись куда проще, чем старые трюки с padding-top и абсолютным позиционированием.


    color-scheme

    Что это такое:
    color-scheme говорит браузеру, в каких схемах элемент или страница должны отображаться: light, dark или оба варианта. Это особенно полезно для нативных элементов интерфейса — полей, кнопок, скроллбаров и прочих встроенных частей.

    Как было раньше:

    Код:
    <style>
      .panel {
        background: #111;
        color: #eee;
        padding: 16px;
        border-radius: 16px;
      }
    </style>
    
    <div class="panel">
      <input type="text" placeholder="Ваше имя">
      <textarea placeholder="Комментарий"></textarea>
    </div>
    

    Проблема была в том, что часть встроенных элементов могла выглядеть не очень согласованно с общей темой.

    Как можно сейчас:

    Код:
    <style>
      .panel {
        color-scheme: dark;
        background: #111;
        color: #eee;
        padding: 16px;
        border-radius: 16px;
      }
    </style>
    
    <div class="panel">
      <input type="text" placeholder="Ваше имя">
      <textarea placeholder="Комментарий"></textarea>
    </div>
    

    Результат:

    [html]
    <div class="demo demo-scheme">
      <div class="panel">
        <div class="panel__title">Настройки профиля</div>
        <input type="text" placeholder="Ваше имя">
        <textarea placeholder="Пара слов о себе"></textarea>
        <button type="button">Сохранить</button>
      </div>
    </div>

    <style>
      .demo-scheme {
        padding: 20px;
        border: 1px solid #e6e6e6;
        border-radius: 18px;
        background: #f6f6f6;
        font-family: Arial, sans-serif;
      }

      .panel {
        max-width: 360px;
        color-scheme: dark;
        background: #111827;
        color: #eef2ff;
        padding: 18px;
        border-radius: 16px;
      }

      .panel__title {
        margin-bottom: 12px;
        font-size: 18px;
        font-weight: 700;
      }

      .panel input,
      .panel textarea,
      .panel button {
        width: 100%;
        margin-top: 10px;
        font: inherit;
      }

      .panel textarea {
        min-height: 90px;
        resize: vertical;
      }

      .panel button {
        padding: 10px 14px;
        border: 0;
        border-radius: 10px;
        background: #6366f1;
        color: #fff;
        cursor: pointer;
      }
    </style>
    [/html]

    Если хочется поддерживать обе темы, можно написать так:

    Код:
    <style>
      .app {
        color-scheme: light dark;
      }
    
      @media (prefers-color-scheme: dark) {
        .app {
          background: #111;
          color: #eee;
        }
      }
    </style>
    

    То есть color-scheme — это не замена полноценной тёмной теме, а способ аккуратнее подружить ваш интерфейс с браузерным UI.


    Итог:

    [list]
    [*]CSS Nesting и & позволяют писать меньше повторов и делают структуру стилей заметно чище.
    [*]@layer помогает навести порядок в каскаде и уменьшает боль от специфичности.
    [*]:is() и :where() делают селекторы короче и аккуратнее, а заодно дают контроль над специфичностью.
    [*]:has() позволяет стилизовать родителя в зависимости от содержимого.
    [*]@container делает компоненты по-настоящему самостоятельными.
    [*]dvh / lvh / svh упрощают работу с мобильной высотой.
    [*]aspect-ratio делает работу с пропорциями заметно проще.
    [*]color-scheme помогает аккуратно встроить светлую и тёмную тему в браузерный интерфейс.
    [/list]

    В общем, современный CSS стал заметно приятнее, чем был ещё несколько лет назад. И пусть далеко не каждый день приходится использовать все эти возможности разом, знать о них всё равно полезно: иногда одна такая мелочь способна заметно упростить и код, и жизнь.

    0

    6

    Код:
    [table widths=160px,30%,auto]
    [tr][th bg=#f2f2f2]Имя[/th][th]Описание[/th][th]Статус[/th][/tr]
    [tr][td bg=#fff7d6]Алиса[/td][td]Текст[/td][td bg=#e7ffe7]Активно[/td][/tr]
    [/table]
    
    [table]
    [tr][th]Заголовок[/th][th]Заголовок[/th][/tr]
    [tr][td]Ячейка[/td][td]Ячейка[/td][/tr]
    [tr][td]Ячейка[/td][td]Ячейка[/td][/tr]
    [tr][td]Ячейка[/td][td]Ячейка[/td][/tr]
    [/table]
    
    [table widths=80%,15%]
    [tr][th]Заголовок[/th][th]Заголовок[/th][/tr]
    [tr][td]Ячейка[/td][td]Ячейка[/td][/tr]
    [tr][td]Ячейка[/td][td]Ячейка[/td][/tr]
    [tr][td]Ячейка[/td][td]Ячейка[/td][/tr]
    [/table]

    0


    Вы здесь » Kindred Souls » Доска объявлений » Нужные