Flex 弹性布局

完整控制方向、对齐与分布的 flex 容器。

ATRI

科幻连载中
<script setup lang="ts">
  import { Button, Card, Flex, Heading, Tag } from '@hina-ui/vue'
</script>

<template>
  <Card class="w-full max-w-lg">
    <Flex justify="between" align="center" wrap class="gap-3">
      <Flex direction="col" gap="xs">
        <Heading :level="3" size="md">ATRI</Heading>
        <Flex gap="xs">
          <Tag>科幻</Tag>
          <Tag>连载中</Tag>
        </Flex>
      </Flex>
      <Flex gap="sm">
        <Button variant="soft" tone="neutral">加入书架</Button>
        <Button>开始阅读</Button>
      </Flex>
    </Flex>
  </Card>
</template>
vue

用法

import { Flex } from '@hina-ui/vue'
ts

Flex 开放了 flex 布局的四个属性:direction、align、justify 和 wrap。方向固定时用 Stack 或 Inline 更简洁,需要切换方向或完整控制对齐时用 Flex。

<script setup lang="ts">
  import { Card, Flex } from '@hina-ui/vue'
</script>

<template>
  <Flex gap="sm">
    <Card class="bg-inset size-10" :padded="false" />
    <Card class="bg-inset size-10" :padded="false" />
    <Card class="bg-inset size-10" :padded="false" />
  </Flex>
</template>
vue

示例

方向

direction 有四个取值,默认横排。

row

1
2
3

row-reverse

1
2
3

col

1
2
3

col-reverse

1
2
3
<script setup lang="ts">
  import { Card, Flex, Stack, Text } from '@hina-ui/vue'

  const directions = ['row', 'row-reverse', 'col', 'col-reverse'] as const
</script>

<template>
  <Flex gap="lg" wrap>
    <Stack v-for="direction in directions" :key="direction" gap="xs">
      <Text tone="muted" size="sm">{{ direction }}</Text>
      <Flex :direction="direction" gap="sm" class="bg-inset rounded-md p-3">
        <Card class="bg-surface grid size-8 place-items-center text-sm" :padded="false">1</Card>
        <Card class="bg-surface grid size-8 place-items-center text-sm" :padded="false">2</Card>
        <Card class="bg-surface grid size-8 place-items-center text-sm" :padded="false">3</Card>
      </Flex>
    </Stack>
  </Flex>
</template>
vue

间距

gap 只接受档位。md 档随方向取值:横排取 --hn-inline-gap,纵排取 --hn-stack-gap,因此方向变化时无需调整间距。其余档位在两个方向上一致,分别是 0、4、8、24 和 32 像素。

横向的 md 档取 12 像素

纵向的 md 档取 16 像素

<script setup lang="ts">
  import { Card, Flex, Stack, Text } from '@hina-ui/vue'
</script>

<template>
  <Flex gap="xl" wrap>
    <Stack gap="xs">
      <Text tone="muted" size="sm">横向的 md 档取 12 像素</Text>
      <Flex gap="md" class="bg-inset rounded-md p-3">
        <Card class="bg-surface size-8" :padded="false" />
        <Card class="bg-surface size-8" :padded="false" />
        <Card class="bg-surface size-8" :padded="false" />
      </Flex>
    </Stack>
    <Stack gap="xs">
      <Text tone="muted" size="sm">纵向的 md 档取 16 像素</Text>
      <Flex direction="col" gap="md" class="bg-inset rounded-md p-3">
        <Card class="bg-surface size-8" :padded="false" />
        <Card class="bg-surface size-8" :padded="false" />
        <Card class="bg-surface size-8" :padded="false" />
      </Flex>
    </Stack>
  </Flex>
</template>
vue

对齐与分布

align 控制交叉轴,justify 控制主轴。横排时 align 作用于纵向、justify 作用于横向,纵排时两者对调。

align 管交叉轴

start

center

end

stretch

justify 管主轴

<script setup lang="ts">
  import { Card, Flex, Stack, Text } from '@hina-ui/vue'

  const aligns = ['start', 'center', 'end', 'stretch'] as const
  const justifies = ['start', 'center', 'end', 'between'] as const
</script>

<template>
  <Stack>
    <Stack gap="xs">
      <Text tone="muted" size="sm">align 管交叉轴</Text>
      <Flex gap="lg" wrap>
        <Stack v-for="align in aligns" :key="align" gap="xs">
          <Text tone="muted" size="sm">{{ align }}</Text>
          <Flex :align="align" gap="sm" class="bg-inset h-24 rounded-md p-3">
            <Card class="bg-surface h-8 w-8" :padded="false" />
            <Card class="bg-surface h-12 w-8" :padded="false" />
          </Flex>
        </Stack>
      </Flex>
    </Stack>
    <Stack gap="xs">
      <Text tone="muted" size="sm">justify 管主轴</Text>
      <Flex direction="col" gap="sm">
        <Flex
          v-for="justify in justifies"
          :key="justify"
          :justify="justify"
          class="bg-inset w-64 rounded-md p-3"
        >
          <Card class="bg-surface size-8" :padded="false" />
          <Card class="bg-surface size-8" :padded="false" />
        </Flex>
      </Flex>
    </Stack>
  </Stack>
</template>
vue

换行

不设置 wrap 时不换行,这是 flex 自身的行为。Inline 默认换行,Flex 则不默认换行。

wrap 为真时换行

默认不换行,保持在一行

<script setup lang="ts">
  import { Card, Flex, Stack, Text } from '@hina-ui/vue'

  const items = Array.from({ length: 10 }, (_, i) => i + 1)
</script>

<template>
  <Stack class="w-full max-w-xs">
    <Stack gap="xs">
      <Text tone="muted" size="sm">wrap 为真时换行</Text>
      <Flex wrap gap="sm">
        <Card v-for="i in items" :key="i" class="bg-inset size-10" :padded="false" />
      </Flex>
    </Stack>
    <Stack gap="xs">
      <Text tone="muted" size="sm">默认不换行,保持在一行</Text>
      <Flex gap="sm" class="overflow-hidden">
        <Card v-for="i in items" :key="i" class="bg-inset size-10 shrink-0" :padded="false" />
      </Flex>
    </Stack>
  </Stack>
</template>
vue

随断点变化

方向与对齐在不同断点下不同时,用 Tailwind 的断点类覆盖,例如 sm:flex-row。属性本身不接受响应式对象。

ATRI

窄屏时封面在上、文字在下,宽屏时并排。断点用 Tailwind 的类覆盖 direction。

<script setup lang="ts">
  import { Card, Flex, Heading, Text } from '@hina-ui/vue'
</script>

<template>
  <Flex direction="col" gap="md" class="w-full max-w-lg sm:flex-row sm:items-center">
    <Card class="bg-inset h-24 shrink-0 sm:w-32" :padded="false" />
    <Flex direction="col" gap="xs">
      <Heading :level="3" size="md">ATRI</Heading>
      <Text tone="muted" size="sm">
        窄屏时封面在上、文字在下,宽屏时并排。断点用 Tailwind 的类覆盖 direction。
      </Text>
    </Flex>
  </Flex>
</template>
vue

根元素

as 接受标签名或 Vue 组件,例如 as="section"、:as="RouterLink" 或 :as="NuxtLink"。组件须先导入或通过 resolveComponent 解析;对象组件、函数式组件和异步组件均可使用。布局类名、额外属性、事件监听器和默认插槽会传给该组件,不增加包裹层;自定义组件需将属性和类名传到实际根元素。

API

属性
类型
默认值
说明
direction
'row' | 'col' | 'row-reverse' | 'col-reverse'
'row'
主轴方向
gap
'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'
—
子元素之间的间距
align
'start' | 'center' | 'end' | 'baseline' | 'stretch'
—
交叉轴对齐方式
justify
'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'
—
主轴分布方式
wrap
boolean
—
放不下时是否换行
as
string | Component
'div'
渲染的标签或 Vue 组件
class
string
—
追加至容器的类名
插槽
说明
default
子元素