grid сетка —— Макет
Сетка — это каркас раскладки: страница режется на 24 колонки, содержимое располагается пропорционально ширине колонок и адаптируется по брейкпоинтам. Сетка lh-ui построена на CSS Grid (не на процентных ширинах flex) — выше точность позиционирования, поддерживается явное задание начальной точки. core/grid отвечает за определение брейкпоинтов, адаптивный откат и клампинг-расчёт расположения колонок — сам CSS не умеет ни «откатываться вниз», ни «клампить при выходе за границы».
Когда использовать
Заголовок раздела «Когда использовать»- Реализация
<LhRow>/<LhCol>на всех концах: основной каркас страницы, двух-/трёхколоночные раскладки форм, карточная стена, дашборды. - Нужны адаптивные ширины колонок (на ПК три колонки, на планшете две, на телефоне одна).
- Не следует использовать: простое выравнивание двух элементов (flex или
spaceподойдут напрямую); поточная раскладка с нефиксированной шириной (фиксированные пропорции колонок grid будут только мешать).
import { LH_GRID_COLUMNS, LH_GRID_BREAKPOINTS, LH_GRID_BREAKPOINT_ORDER, resolveBreakpoint, resolveResponsiveSize, getGridRowClassNames, normalizeColSize, clampSpan, getColStyle, getGridColClassNames,} from '@lh-ui/share/core/grid'import type { LhGridBreakpoint, LhGridSpan, LhColSize, LhBreakpointSizes,} from '@lh-ui/share/core/grid'Замысел
Заголовок раздела «Замысел»CSS Grid, а не процентный flex: в flex-сетке каждая колонка имеет width: x/24, переносы строк делаются через flex-wrap, а межколоночные отступы компенсируются отрицательным margin — набор хаков. CSS Grid напрямую задаёт grid-template-columns: repeat(24, 1fr), отступы через gap, колонки располагаются через grid-column — семантика чистая. Расплата — отсутствие поддержки в IE, но все целевые концы lh-ui (современные браузеры + webview мини-программ) уже покрыты.
Откат брейкпоинта вниз: resolveResponsiveSize(sizes, 'md') идёт от ступени md в сторону уменьшения и берёт первую объявленную ступень. Если пользователь указал только { xs: 24, lg: 12 }, при ширине md (768~1024) произойдёт откат к xs, то есть 24 — не undefined. Это ключевой опыт адаптивного API: пользователь объявляет только ключевые брейкпоинты, а промежуточные ступени автоматически наследуют конфигурацию для меньших экранов.
span = 0 — это сигнальное значение, а не баг: span: 0, offset: 6 означает «не занимать колонки, только сдвинуть последующее содержимое на 6 колонок». При этом getColStyle выведет 7 / 25 (с продлением до последней колонки), а не 7 / span 0 — последнее схлопнуло бы элемент в нулевую ширину.
Клампинг начальной точки защищает от разрыва контейнера: offset 21 + span 8 даёт начальную точку 22 — за пределами сетки. getColStyle клампит старт в 24 + 1 - 8 = 17 — элемент целиком сдвигается влево до тех пор, пока не поместится. В комментарии указана причина: вышедший за границы grid-column заставляет Grid создать неявный трек и разорвать ширину контейнера.
Раздел между авто-размещением и явным позиционированием: чистый span использует span N (без указания точки старта, Grid размещает автоматически); наличие offset/push/pull — start / span N (явная начальная точка, прерывающая авто-размещение). В комментарии зафиксирована реальная ловушка: если чистый span записать как 1 / span N, все Col в строке начнутся с колонки 1, и Grid вытолкнет остальные на следующую строку — визуально получится «вертикальный столбец».
Таблица имён классов вместо прямой конкатенации: если бы justify="center" и align="center" склеивали имена напрямую, получился бы дубль lh-row--center (justify работает через justify-items, align — через align-items, это два разных CSS-свойства). С явной таблицей отображения: center для justify → lh-row--center, center для align → lh-row--middle (center как совместимый алиас middle, в духе привычных API популярных библиотек). Побочный плюс: неизвестные значения не порождают грязные имена классов.
Таблица функций
Заголовок раздела «Таблица функций»| Функция / константа | Сигнатура | Возврат | Назначение |
|---|---|---|---|
resolveBreakpoint |
(width: number) => LhGridBreakpoint |
брейкпоинт | ширина вьюпорта → ступень |
resolveResponsiveSize |
(sizes, breakpoint) => LhColSize | undefined |
действующий размер | откат вниз до ближайшей объявленной ступени |
getGridRowClassNames |
(state: { justify?, align?, wrap? }) => string[] |
классы | выравнивание/перенос |
normalizeColSize |
(size?: LhColSize) => LhGridSpan |
объект-описание | число → { span } |
clampSpan |
(span?: number) => number |
целое в [0, 24] | неконечные значения → 0 |
getColStyle |
(size?: LhColSize) => Record<string, string> |
инлайновые стили | расположение grid-column |
getGridColClassNames |
(size?: LhColSize) => string[] |
классы | span / offset |
Константы: LH_GRID_COLUMNS = 24, LH_GRID_BREAKPOINTS (xs 0 / sm 640 / md 768 / lg 1024 / xl 1280), LH_GRID_BREAKPOINT_ORDER (от большего к меньшему).
Определения типов
Заголовок раздела «Определения типов»export type LhGridBreakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
export interface LhGridSpan { span?: number // количество занимаемых колонок (0 = не занимать, используется только для позиционирования offset) offset?: number // количество пустых колонок слева push?: number // сдвиг начальной точки вправо pull?: number // сдвиг начальной точки влево}
export type LhColSize = number | LhGridSpanexport type LhBreakpointSizes = Partial<Record<LhGridBreakpoint, LhColSize>>Доступность
Заголовок раздела «Доступность»Сетка — это примитив чистой раскладки, не имеющий собственной семантики доступности — не добавляйте Row/Col роль. Семантику содержимого предоставляют внутренние компоненты. Один сопутствующий принцип:
- Изменение раскладки не должно менять порядок DOM: при адаптивном переупорядочивании порядок Col в DOM фиксирован, меняется только визуальное количество колонок. Если на разных брейкпоинтах требуется разный порядок содержимого («основной контент на ПК слева, на мобильном вверху»), используйте порядок в исходном коде + стиль
order, а не переключайте порядок DOM — скринридер зачитывает в порядке DOM, и несовпадение визуального порядка с порядком озвучивания вызовет путаницу.
Примеры использования
Заголовок раздела «Примеры использования»import { resolveBreakpoint, resolveResponsiveSize, getColStyle, clampSpan,} from '@lh-ui/share/core/grid'
resolveBreakpoint(800) // 'md'resolveBreakpoint(600) // 'xs' (< 640)resolveBreakpoint(1300) // 'xl'
// Откат вниз: ступень md не объявлена → берётся xsresolveResponsiveSize({ xs: 24, lg: 12 }, 'md') // 24resolveResponsiveSize({ xs: 24, lg: 12 }, 'lg') // 12resolveResponsiveSize({ xs: 24 }, 'xl') // 24resolveResponsiveSize(undefined, 'md') // undefined (нет объявлений)getColStyle(8) // { gridColumn: 'span 8' } (авто-размещение)getColStyle({ span: 8, offset: 4 })// { gridColumn: '5 / span 8' } (старт = offset+1, после клампинга границ не нарушены)getColStyle({ span: 0, offset: 6 })// { gridColumn: '7 / 25' } (сигнальное значение: Colon не занимаются, продление до последней)getColStyle({ span: 8, offset: 21 })// { gridColumn: '17 / span 8' } (клампинг при выходе за границу: старт 22 → 17)getColStyle({ span: 8, push: 4 })// { gridColumn: '5 / span 8' } (push 4 = сдвиг начальной точки вправо на 4)getColStyle({ span: 8, pull: 4 })// { gridColumn: '1 / span 8' } (старт 5-4=1, нижняя граница клампинга 1)getColStyle({}) // { gridColumn: 'span 24' } (без объявлений — на всю ширину)
clampSpan(8) // 8clampSpan(30) // 24 (кламп до верхнего предела)clampSpan(-5) // 0clampSpan(NaN) // 0 (неконечное значение → 0)<!-- Vue3: адаптивные три колонки → две → одна --><template> <LhRow :gutter="16"> <LhCol v-for="item in list" :key="item.id" :xs="24" :sm="12" :lg="8"> <LhCard>{{ item.title }}</LhCard> </LhCol> </LhRow></template><!-- Двухколоночная раскладка формы + кнопка с позиционированием offset --><template> <LhRow :gutter="20"> <LhCol :span="12"><LhFormItem label="Имя" prop="name" /></LhCol> <LhCol :span="12"><LhFormItem label="Эл. почта" prop="email" /></LhCol> <LhCol :span="12" :offset="12"> <LhButton type="primary" @click="submit">Отправить</LhButton> </LhCol> </LhRow></template>// React: слежение за брейкпоинтами ведёт UI-слой через resize-листенер или useMediaQuery,// управляющий state; getColStyle вызывается как чистая функция в render// uniapp: <view> + grid; на стороне мини-программ нет window, брейкпоинт// определяется UI-слоем по safeArea/windowWidth из uni.getSystemInfoSync()// или пересчётом res.rpx, а resolveBreakpoint из core используется как естьСтили и токены
Заголовок раздела «Стили и токены»| Класс | Семантика |
|---|---|
.lh-row |
контейнер строки (display: grid; grid-template-columns: repeat(24, 1fr)) |
.lh-row--{end|center|space-between|space-around} |
горизонтальное выравнивание (justify-items) |
.lh-row--{middle|bottom} |
вертикальное выравнивание (align-items) |
.lh-row--nowrap |
без переноса |
.lh-col |
контейнер колонки |
.lh-col--{1..24} |
количество колонок span |
.lh-col--offset-{1..24} |
количество колонок смещения слева |
Токен отступов --lh-row-gutter (междустрочный интервал, переопределяется prop gutter); промежутки между колонками реализуются через grid gap.
Замечания
Заголовок раздела «Замечания»gutterи grid gap: gap в Grid действует одновременно на строки и колонки; если нужен только межколоночный отступ без межстрочного, контейнер строки нужно отдельно переопределитьrow-gap: 0.- За брейкпоинтами должен следить UI-слой:
resolveBreakpoint— чистая функция, UI-слой должен сам слушать resize (или использоватьmatchMedia) и подавать ширину. При SSR на первом экране нет window, по умолчанию рендерится ступень xs, а после гидратации на клиенте значение исправляется. - Не смешивайте авто-размещение и явное позиционирование: если в одной строке один Col использует чистый span, а другой — offset, явно позиционированный прервёт авто-размещение — результат размещения в строке может оказаться неочевидным. Либо все с чистым span, либо все с явным позиционированием.
- Разница между
push/pullиoffset: все три меняют начальную точку, но семантика разная —offsetоставляет «пустое место слева» (семантика резерва),push/pull— «сдвиг» (визуальное переупорядочивание). Реализация вgetColStyleвсюду одна — сдвиг старта, на уровне имён классов соответствует только offset (push/pull идут только через инлайновые стили). - 24-колоночная система координат — жёсткое ограничение:
span 25будет заклампен к 24;offset 24 + span 1— старт клампится к 24. Если бизнесу нужна более мелкая дискретность свыше 24, используйте вложенностьLhRow, а не увеличивайте количество колонок. - Вложенные сетки: внутри
LhColснова размещаетсяLhRow, и внутренняя сетка начинает отсчёт заново с 24 колонок — дочерняя сетка не наследует span-контекст родительской. Это естественное поведение CSS Grid. - Сосуществование класса span и инлайнового стиля:
lh-col--8изgetGridColClassNamesиgrid-columnизgetColStyle— два механизма (классы для медиавыражений SCSS, инлайн для адаптива из JS). UI-слой выбирает один из двух: одновременная запись обоих приведёт к взаимному перекрытию (инлайновый стиль победит).
@lh-ui/share/core/grid → getColStyle( )(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue