GrLink

  • ядро
  • Действия
  • API · 13 props

Берут, когда нужен переход.

import { GrLink } from '@feugene/granularity/components/GrLink'

Обзор

Когда брать

  • нужен переход — ссылка меняет адрес, и открытие в новой вкладке обязано работать;
  • роутер свой — as подменяет корневой тег на RouterLink или NuxtLink без обёртки;
  • ссылка ведёт наружу — external добавляет значок и подпись «в новой вкладке» для скринридера;
  • ссылка живёт в тексте — underline и tone держат её узнаваемой внутри абзаца.

Когда взять другое

НужноБерите
Действие меняет состояние, а не адресGrButton
Ссылка выглядит кнопкойGrButton с as/href
Список переходовGrSidebar / GrDropdownMenu
Путь до текущей страницыGrBreadcrumbs

Кнопка меняет состояние, ссылка меняет адрес. Подменять одно другим ради внешности нельзя: у ссылки работают средняя кнопка мыши, контекстное меню и «открыть в новой вкладке», у кнопки — нет, и пользователь замечает это первым.

Варианты все тона и виды — настоящим компонентом

Варианты · GrLink
tone \ variantdefaultmuted
primary
neutral
success
warning
danger
info
slate
azure

Размеры

  • xs
  • sm
  • md
  • lg

Состояния

  • обычная
  • disabled

Матрица живая: наводите и нажимайте. Состояний наведения, фокуса и нажатия здесь нет — они за браузером, а не за пропом.

Playground контролы выводятся из API, а не пишутся руками

Сцена

Загружается…

0 изменено· состояние — в адресе

Примеры 3 у каждого — живое демо и его код

API порождается из пакета · разойтись не может

Props

PropTypeпо умолчаниюОписание
disabledboolean | undefinedfalseСсылка недоступна: корень — `<span>` без `href`, вне порядка `Tab`, с `aria-disabled`.
tone
"primary" | "neutral" | "success" | "warning" | "danger" | ""primary" | "neutral" | "success" | "warning" | "danger" | "info" | "slate" | "azure" | undefined
"primary"Семантический цвет ссылки из палитры `GrTone`.
variantGrLinkVariant | undefined"default"Уровень акцента: `default` (окрашен) или `muted` (приглушён, акцент на hover).
size"xs" | "sm" | "md" | "lg" | undefinedundefinedКегль ссылки и её иконки. По умолчанию `md`; берётся из `GrConfigProvider`.
ariaLabelstring | undefinedundefinedДоступное имя вместо текста; подсказка о новой вкладке дописывается к нему сама.
asstring | Component | undefinedundefinedКастомный корневой тег/компонент. Если передан и компонент не `disabled` — рендерится через `<component :is="as">`. Игнорируется при `disabled`.
hrefstring | undefinedundefinedАдрес ссылки: без `as` корень становится `<a>`. При `disabled` не выводится.
targetstring | undefinedundefined`target` ссылки, сильнее `external`. С `_blank` добавляются защитный `rel` и подсказка.
relstring | undefinedundefined`rel` ссылки. Без него при `target="_blank"` ставится `noopener noreferrer`.
externalboolean | undefinedfalseОткрыть в новой вкладке: `target="_blank"`, `rel="noopener noreferrer"` и иконка.
underlineGrLinkUnderline | undefined"auto"Подчёркивание: `auto` — при наведении, `always` — всегда, для ссылки посреди текста, `none` — никогда. По умолчанию `auto`.
externalIconboolean | undefinedundefinedИконка внешней ссылки. По умолчанию показывается у любой ссылки, которая открывается в новой вкладке, — не только при `external`.
newTabLabelstring | undefinedundefinedi18n: скрытая подсказка «откроется в новой вкладке».

Слоты

SlotTypeОписание
defaultanyТекст ссылки.

Типы

Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.

Импорт
import type { GrLinkTone, GrLinkVariant, GrLinkUnderline } from '@feugene/granularity/components/GrLink'

Файлdist/types/components/GrLink/config.d.ts

grLinkConfigconst

dynamicTokens — имена, которые собираются в рантайме: цвет тона читается как var(--gr-${tone}-text), и статический анализ видит от имени только префикс --gr-. Объявление снимает находку token-undefined на этом префиксе и сообщает обрезке токенов, что весь набор -text компоненту нужен.

const grLinkConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrLink">;

Файлdist/types/components/GrLink/grLinkStyles.d.ts

GrLinkTonetype

Архитектура цвета GrLink (согласована с остальной GR, ср. GrButton): - tone — семантический ЦВЕТ из общей палитры GrTone (primary | neutral | success | warning | danger | info | slate | azure). - variant — уровень акцента (СТРУКТУРА), независимый от цвета: - default — текст окрашен в tone в покое, tone-hover/tone-active при наведении/нажатии (обычная акцентная ссылка); - muted — приглушённый текст (--gr-muted-fg) в покое, окрашивается в tone при наведении (вторичные/inline-ссылки в плотных областях). Оси ортогональны намеренно: «красная ссылка» и «приглушённая ссылка» — независимые решения, и любая их комбинация осмысленна.

type GrLinkTone = GrTone;
GrLinkVarianttype
type GrLinkVariant = 'default' | 'muted';
GrLinkUnderlinetype
type GrLinkUnderline = 'auto' | 'always' | 'none';
GrLinkSizetype
type GrLinkSize = GrComponentSize;

Файлdist/types/components/GrLink/safelist.d.ts

grLinkSafelistconst
const grLinkSafelist: string[];

Файлdist/types/components/GrLink/defaults.d.ts

GrLinkConfigurablePropsinterface

Пропы GrLink, настраиваемые глобально через <GrConfigProvider :component-defaults="{ GrLink: { … } }">. Только оформление — см. GrButton/defaults.ts.

interface GrLinkConfigurableProps {
    size: GrLinkSize;
}

Файлdist/types/components/GrLink/GrLink.vue.d.ts

GrLinkPropsinterface
interface GrLinkProps {
    /**
    * Кастомный корневой тег/компонент. Если передан и компонент не `disabled` —
    * рендерится через `<component :is="as">`. Игнорируется при `disabled`.
    */
    as?: string | Component;
    /** Адрес ссылки: без `as` корень становится `<a>`. При `disabled` не выводится. */
    href?: string;
    /** Открыть в новой вкладке: `target="_blank"`, `rel="noopener noreferrer"` и иконка. */
    external?: boolean;
    /** `target` ссылки, сильнее `external`. С `_blank` добавляются защитный `rel` и подсказка. */
    target?: string;
    /** `rel` ссылки. Без него при `target="_blank"` ставится `noopener noreferrer`. */
    rel?: string;
    /** Ссылка недоступна: корень — `<span>` без `href`, вне порядка `Tab`, с `aria-disabled`. */
    disabled?: boolean;
    /** Доступное имя вместо текста; подсказка о новой вкладке дописывается к нему сама. */
    ariaLabel?: string;
    /** Семантический цвет ссылки из палитры `GrTone`. */
    tone?: GrLinkTone;
    /** Уровень акцента: `default` (окрашен) или `muted` (приглушён, акцент на hover). */
    variant?: GrLinkVariant;
    /**
     * Подчёркивание: `auto` — при наведении, `always` — всегда, для ссылки посреди текста, `none` —
     * никогда. По умолчанию `auto`.
     */
    underline?: GrLinkUnderline;
    /** Кегль ссылки и её иконки. По умолчанию `md`; берётся из `GrConfigProvider`. */
    size?: GrLinkSize;
    /**
    * Иконка внешней ссылки. По умолчанию показывается у любой ссылки, которая
    * открывается в новой вкладке, — не только при `external`.
    */
    externalIcon?: boolean;
    /** i18n: скрытая подсказка «откроется в новой вкладке». */
    newTabLabel?: string;
}

Использование из прозы библиотеки

Корневой тег
УсловиеЧто рендерится
задан as и не disabled<component :is="as"> — RouterLink, Link от Inertia, любой свой
задан href и не disabled<a>
остальное, включая disabled<span>

Отключённая ссылка намеренно перестаёт быть <a>: так она не кликабельна и не участвует в порядке табуляции. Внешние CSS-селекторы это должны учитывать.

Атрибуты роутера (method, replace, preserve-scroll) проходят через fallthrough — компонент их не перечисляет.

Новая вкладка объявляется

Ссылка, открывающаяся в новой вкладке, получает две вещи:

  • иконку внешней ссылки — видимый признак;
  • скрытый суффикс «(откроется в новой вкладке)» — предупреждение о смене контекста (WCAG 3.2.5), без него переход в новую вкладку для незрячего пользователя происходит без предупреждения.

Условие — фактическое поведение ссылки (target="_blank"), а не проп external: target снаружи даёт ровно тот же сюрприз. По той же причине компонент выставляет rel="noopener noreferrer" любой ссылке с _blank, а не только external.

vue
<GrLink href="https://example.com" external>
Документация
</GrLink>

<GrLink href="https://example.com" target="_blank">
То же самое
</GrLink>

<!-- Иконку можно выключить или, наоборот, включить внутренней ссылке. -->
<GrLink href="https://example.com" external :external-icon="false">
Без иконки
</GrLink>

<GrLink href="/inner" external-icon>
С иконкой
</GrLink>

newTabLabel перекрывает текст подсказки, ключ локали — gr.link.opensInNewTab.

ariaLabel и подсказка

aria-label перекрывает содержимое элемента целиком — вместе со скрытым суффиксом. Поэтому, если имя задано вручную, подсказка дописывается к нему: aria-label="Документация" у внешней ссылки превращается в «Документация, откроется в новой вкладке», а отдельный sr-only-суффикс не рендерится, чтобы диктор не прочитал его дважды.

Цвет: tone × variant

Оси ортогональны. tone — цвет из общей палитры (primary, neutral, success, warning, danger, info, slate, azure), variant — уровень акцента: default (окрашен в покое) или muted (приглушён, окрашивается при наведении).

Цвет прокидывается CSS-переменными (--gr-link-color, -hover, -active), а не классами на каждую комбинацию: 8 тонов × 3 состояния дали бы class-эксплозию, а safelist остаётся крошечным.

Тона берутся из ролей -text, а не из насыщенных --gr-{tone}: ссылка — это текст на фоне страницы, а --gr-success на --gr-bg даёт 2.2:1.

Подчёркивание и disabled

underline: auto (появляется на hover), always, none.

Отключённая ссылка гасится цветом --gr-muted-fg, а не opacity: прозрачность разбавляет выверенный на AA токен и роняет контраст ниже нормы.