suiss/ui
  • Docs
  • Components
  • Patterns
  • Interactions
  • Charts
  • Blocks
GitHubGet started
DocsComponentsPatternsInteractionsChartsBlocks
Components82
  • Overview
  • Accordion
  • Alert
  • Alert Dialog
  • Alert Sheet
  • Amount Input
  • Aspect Ratio
  • Attachment
  • Avatar
  • Badge
  • Bottom Bar
  • Breadcrumb
  • Bubble
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Chip
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • FAB
  • Field
  • Fullscreen Menu
  • Haptics
  • Header
  • Hover Card
  • Icon
  • Input
  • Input Accessory Bar
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Keyboard Scroll View
  • Keypad
  • Label
  • Marker
  • Menubar
  • Message
  • Message Scroller
  • Native Select
  • Navigation Menu
  • Page Indicator
  • Pagination
  • Pin Pad
  • Popover
  • Progress
  • QR Scanner
  • Questionnaire
  • Radio Group
  • Resizable
  • Screen
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Signature Pad
  • Skeleton
  • Slider
  • Spinner
  • Switch
  • Tab Bar
  • Table
  • Tabs
  • Text
  • Textarea
  • Toast
  • Toggle
  • Toggle Group
  • Tooltip
  • Wheel Picker
  • Widget

suiss/ui · Component

Marker

An inline annotation for timelines and conversations, such as a date divider or system event.

Marker is an open-source component from suiss/ui for React, React Native, SwiftUI and Jetpack Compose. The @esuiss/ui CLI copies its source into your project, so you own the code.

Marker displays a short line of secondary text, typically between messages or list entries. Put the text in MarkerContent and, optionally, a leading MarkerIcon.

The variant prop changes the treatment: plain text, text centered between two separator rules, or text above a bottom border. Use Marker for annotations within content; use Separator for a purely visual rule with no text.

Installation

CLI

$ pnpm dlx shadcn@latest add @esuiss/marker

Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add marker

Manual

  1. Install the dependencies and add the cn helper:
$ pnpm add @base-ui/react class-variance-authority
  1. Copy the source into components/ui/marker.tsx:
  1. Update the import paths to match your project.

Usage

import { Marker, MarkerIcon, MarkerContent } 
<div



Links inside the marker or its content are underlined automatically. The underlying element can be changed:

Marker is built on Base UI's useRender, so it accepts a render prop to change the underlying element.

Set asChild on Marker and pass the element as the child.

Pass a render function to Marker that returns the element with the props it receives.

Examples

Variants

The default, separator and border variants stacked for comparison.

Separator

Text centered between two horizontal rules, as used for a date divider.

With icon

Markers with a leading decorative icon in the default and separator variants.

With link

A marker whose content includes an inline link.

API Reference

Anatomy3 parts
<Marker>
<MarkerContent />
<MarkerIcon />
</Marker>
01

Marker

The container row that applies the variant styling.

PropTypeDefaultDescription
variant"default" | "separator" | "border""default"Sets the visual treatment: default for plain text, separator for text between two rules, or border for text above a bottom border.

Also accepts the props of useRender.ComponentProps<"div">.

02

MarkerIcon

A decorative icon slot shown before the content and hidden from assistive technology.

Also accepts the props of React.ComponentProps<"span">.

03

MarkerContent

Holds the marker text and any inline links.

Also accepts the props of React.ComponentProps<"span">.

Accessibility

  • MarkerIcon sets aria-hidden="true", so icons must be decorative; put meaningful text in MarkerContent.
  • Links inside the content remain in the normal tab order.

Notes

  • Styling hooks: data-slot="marker", data-slot="marker-icon" and data-slot="marker-content". The root also exposes data-variant.
  • Rules and borders use the border color; text uses muted-foreground and links hover to foreground.

Marker is a row of secondary text with an optional icon. It has three variants: default for a plain row, separator for centered text with a line on each side, and border for a row with a bottom border.

Use separator for date dividers in a chat or feed, border for section headings, and default for short notices.

Installation

$ pnpm dlx shadcn@latest add @esuiss-rn/marker

See Installation for the components.json setup. Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add marker
$ pnpm dlx @esuiss/ui@latest add marker --platform swift
NextMenubarPreviousLabel

On this page

  1. 01Installation
    • CLI
    • Manual
  2. 02Usage
  3. 03Examples
    • Variants
    • Separator
    • With icon
    • With link
  4. 04API Reference
    • Marker
    • MarkerIcon
    • MarkerContent
  5. 05Accessibility
  6. 06Notes
  7. 07Installation
    • Manual
  8. 08Usage
  9. 09API Reference
    • Marker
    • MarkerIcon
    • MarkerContent
  10. 10Accessibility
  11. 11Notes
from
"@/components/ui/marker"
className
=
"w-96"
>
<Marker>
<MarkerContent>Conversation started</MarkerContent>
</Marker>
</div>
$ pnpm dlx @esuiss/ui@latest add marker --platform kotlin

The CLI also adds what it builds on: Icon, Text.

Manual

  1. Install the dependencies:
npx expo install class-variance-authority cn
  1. Add the pieces it builds on: Icon, Text, and the primitives components/primitives/types.tsx, components/primitives/use-render.tsx.

  2. Copy the source into components/ui/marker.tsx:

  1. Update the import paths to match your project.
  1. Install the dependencies:
npx expo install class-variance-authority cn
  1. Add the pieces it builds on: Icon, Text, and the primitives components/primitives/types.tsx, components/primitives/use-render.tsx.

  2. Copy the source into components/ui/marker.tsx:

  1. Update the import paths to match your project.
  1. Add the pieces it builds on: Icon, Text, and the primitives components/primitives/types.tsx, components/primitives/use-render.tsx.

  2. Copy the source into components/ui/marker.tsx:

  1. Update the import paths to match your project.
  1. Target iOS 17 or later.

  2. Copy the theme files every view uses:

swift/Theme/Theme.swift
import SwiftUI
import UIKit

struct RGBA: Sendable {
  let red: CGFloat
  let green: CGFloat
  let blue: CGFloat
  let alpha: CGFloat

  init(_ hex: UInt32, _ alpha: CGFloat = 1) {
    red = CGFloat((hex >> 16) & 0xFF) / 255
    green = CGFloat((hex >> 8) & 0xFF) / 255
    blue = CGFloat(hex & 0xFF) / 255
    self.alpha = alpha
  }
swift/Theme/Tokens.swift
import SwiftUI

public enum EsuissColor {
  public static let background = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x000000))
  public static let foreground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let card = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F))
  public static let cardForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let popover = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F))
  public static let popoverForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let primary = Color.adaptive(light: .init(0x0071E3), dark: .init(0x2997FF))
  public static let primaryForeground = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0xFFFFFF))
  public static let secondary = Color.adaptive(light: .init(0x000000, 0.04), dark: .init(0xFFFFFF, 0.1))
  public static let secondaryForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let muted = Color.adaptive(light: .init(0xF5F5F7), dark: .init(0x161617))
  public static let mutedForeground = Color.adaptive(light: .init(0x6E6E73), dark: .init(0xA1A1A6))
  public static let accent = Color.adaptive(light: .init(0x000000, 0.06), dark: .init(0xFFFFFF, 0.1))
  1. Copy the source and every file it uses:
swift/Components/TextVariant.swift
import SwiftUI

public enum TextVariant: CaseIterable, Sendable {
  case `default`, h1, h2, h3, h4, p, blockquote, code, lead, large, small, muted

  var style: TextStyle {
    switch self {
    case .h1: .xl3
    case .h2: .xl2
    case .h3: .xl
    case .h4, .lead, .large: .lg
    case .code, .small, .muted: .sm
    default: .base
    }
  }
  1. Add the files to your app target.
  1. Set minSdk to 24 or higher and add the dependencies:
build.gradle.kts
implementation(platform("androidx.compose:compose-bom:2026.09.00"))
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.ui:ui")
  1. Copy the theme files every view uses:
kotlin/theme/Theme.kt
package com.esuiss.ui.theme

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.dropShadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.shadow.Shadow as DropShadow
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
kotlin/theme/Tokens.kt
package com.esuiss.ui.theme

import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Immutable
data class Colors(
    val background: Color,
    val foreground: Color,
    val card: Color,
    val cardForeground: Color,
    val popover: Color,
    val popoverForeground: Color,
    val primary: Color,
    val primaryForeground: Color,
  1. Copy the source and every file it uses:
kotlin/components/Content.kt
package com.esuiss.ui.components

import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

val LocalContentStyle = compositionLocalOf { TextStyle.Default }

val LocalContentColor = compositionLocalOf { Color.Unspecified }

val LocalIconSize = compositionLocalOf { 20.dp }
kotlin/components/Icon.kt
package com.esuiss.ui.components

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
import com.esuiss.ui.theme.EsuissTheme

@Composable
fun Icon(
    imageVector: ImageVector,
  1. Update the com.esuiss.ui package names to match your project.

Usage

import { Marker, MarkerIcon, MarkerContent } from "@/components/ui/marker"
<View className="gap-8">
  <Marker>
    <MarkerIcon>
      <Icon as={InfoIcon} />
    </MarkerIcon>
    <MarkerContent>Messages are end-to-end encrypted.</MarkerContent>
  </Marker>
  <Marker variant="separator">
    <MarkerContent>Today</MarkerContent>
  </Marker>
  <Marker variant="border">
    <MarkerIcon>
      <Icon as={CalendarBlankIcon} />
    </MarkerIcon>
    <MarkerContent>Upcoming</MarkerContent>
  </Marker>
</View>

Put the text in MarkerContent and an Icon in MarkerIcon. Text and icons inside the marker pick up the text-muted-foreground color and size through context.

Pass a render element to Marker to render it as another component instead of a View.

API Reference

01

Marker

Row that sets the variant and the text and icon styles.

PropTypeDefaultDescription
variant"default" | "separator" | "border""default"default, separator or border.
renderRenderProp<Record<string, unknown>, Record<string, never>>-Element to render in place of the default View.

Also accepts the props of ViewProps.

02

MarkerIcon

Fixed-size slot for an icon.

Also accepts the props of ViewProps.

03

MarkerContent

Marker text, centered in the separator variant.

Also accepts the props of React.ComponentProps<typeof Text>.

Accessibility

  • MarkerIcon and the separator lines are hidden from screen readers, so only the text is read.
  • If the icon carries meaning, include it in the text.

Notes

  • Text uses text-sm text-muted-foreground and icons size-4.
  • Lines and the bottom border use bg-border and border-border.
  • Light and dark colors follow the system appearance.
iOS
Marker (variants) on iOS in light modeMarker (variants) on iOS in dark mode
import { CalendarBlankIcon, InfoIcon } from "phosphor-react-native"
import { View } from "react-native"
import { Icon } from "@/components/ui/icon"
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"

export function MarkerVariantsExample() {
  return (
    <View className="gap-8">
      <Marker>
        <MarkerIcon>
          <Icon as={InfoIcon} />
        </MarkerIcon>
        <MarkerContent>Messages are end-to-end encrypted.</MarkerContent>
      </Marker>
      <Marker variant="separator">
        <MarkerContent>Today</MarkerContent>
      </Marker>
      <Marker variant="border">
        <MarkerIcon>
          <Icon as={CalendarBlankIcon} />
        </MarkerIcon>
        <MarkerContent>Upcoming</MarkerContent>
      </Marker>
    </View>
  )
}
import EsuissUI
import PhosphorSwift
import SwiftUI

struct MarkerVariantsExample: View {
  var body: some View {
    VStack(spacing: 32) {
      Marker {
        MarkerIcon { Ph.info.regular }
        MarkerContent("Messages are end-to-end encrypted.")
      }
      Marker(.separator) {
        MarkerContent("Today")
      }
      Marker(.border) {
        MarkerIcon { Ph.calendarBlank.regular }
        MarkerContent("Upcoming")
      }
    }
    .frame(maxWidth: .infinity)
  }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.adamglin.PhosphorIcons
import com.adamglin.phosphoricons.Regular
import com.adamglin.phosphoricons.regular.CalendarBlank
import com.adamglin.phosphoricons.regular.Info
import com.esuiss.ui.components.Icon
import com.esuiss.ui.components.Marker
import com.esuiss.ui.components.MarkerContent
import com.esuiss.ui.components.MarkerIcon
import com.esuiss.ui.components.MarkerVariant

@Composable
fun MarkerVariants() {
    Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(32.dp)) {
        Marker {
            MarkerIcon { Icon(PhosphorIcons.Regular.Info, contentDescription = null) }
            MarkerContent("Messages are end-to-end encrypted.")
        }
        Marker(variant = MarkerVariant.Separator) {
            MarkerContent("Today")
        }
        Marker(variant = MarkerVariant.Border) {
            MarkerIcon { Icon(PhosphorIcons.Regular.CalendarBlank, contentDescription = null) }
            MarkerContent("Upcoming")
        }
    }
}
import { Marker, MarkerContent } from "@/components/ui/marker"

export default function MarkerDemo() {
  return (
    <div className="w-96">
      <Marker>
        <MarkerContent>Conversation started</MarkerContent>
      </Marker>
    </div>
  )
}
components/ui/marker.tsx
import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cva, type VariantProps } from "class-variance-authority"
import type * as React from "react"
import { cn } from "@/lib/utils"

const markerVariants = cva(
  "group/marker relative flex min-h-4 w-full items-center gap-2 text-start text-sm text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [a]:underline [a]:hover:text-foreground",
  {
    variants: {
      variant: {
        default: "",
        separator:
          "before:me-1 before:h-px before:min-w-0 before:flex-1 before:bg-border after:ms-1 after:h-px after:min-w-0 after:flex-1 after:bg-border",
        border: "border-b border-border pb-2",
      },
import { Marker, MarkerContent } from "@/components/ui/marker"

export default function MarkerVariants() {
  return (
    <div className="w-96">
      <div className="flex flex-col gap-6">
        <Marker variant="default">
          <MarkerContent>Default</MarkerContent>
        </Marker>
        <Marker variant="separator">
          <MarkerContent>Today</MarkerContent>
        </Marker>
        <Marker variant="border">
          <MarkerContent>Earlier messages</MarkerContent>
        </Marker>
      </div>
    </div>
  )
}
import { Marker, MarkerContent } from "@/components/ui/marker"

export default function MarkerSeparator() {
  return (
    <div className="w-96">
      <Marker variant="separator">
        <MarkerContent>Yesterday</MarkerContent>
      </Marker>
    </div>
  )
}
import { CalendarBlankIcon, InfoIcon } from "@phosphor-icons/react"
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"

export default function MarkerWithIcon() {
  return (
    <div className="w-96">
      <div className="flex flex-col gap-6">
        <Marker>
          <MarkerIcon>
            <InfoIcon />
          </MarkerIcon>
          <MarkerContent>Maya joined the conversation</MarkerContent>
        </Marker>
        <Marker variant="separator">
          <MarkerIcon>
            <CalendarBlankIcon />
          </MarkerIcon>
          <MarkerContent>March 12</MarkerContent>
        </Marker>
      </div>
    </div>
  )
}
import { Marker, MarkerContent } from "@/components/ui/marker"

export default function MarkerWithLink() {
  return (
    <div className="w-96">
      <Marker>
        <MarkerContent>
          This chat was archived. <a href="#restore">Restore it</a>
        </MarkerContent>
      </Marker>
    </div>
  )
}
uniwind/components/ui/marker.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "cn"
import * as React from "react"
import { View, type ViewProps } from "react-native"
import type { RenderProp } from "@/components/primitives/types"
import { useRender } from "@/components/primitives/use-render"
import { IconClassContext } from "@/components/ui/icon"
import { Text, TextClassContext } from "@/components/ui/text"

type MarkerVariant = "default" | "separator" | "border"

const MarkerContext = React.createContext<MarkerVariant>("default")

const markerVariants = cva("min-h-4 w-full flex-row items-center gap-2", {
  variants: {
    variant: {
swift/Components/Marker.swift
import SwiftUI

public enum MarkerVariant: CaseIterable, Sendable {
  case `default`, separator, border
}

extension EnvironmentValues {
  @Entry var markerVariant: MarkerVariant = .default
}

public struct Marker<Content: View>: View {
  let variant: MarkerVariant
  let content: Content

  public init(_ variant: MarkerVariant = .default, @ViewBuilder content: () -> Content) {
    self.variant = variant
kotlin/components/Marker.kt
package com.esuiss.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
kotlin/components/Separator.kt
package com.esuiss.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.theme.EsuissTheme

@Composable
fun Separator(
nativewind/components/ui/marker.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "cn"
import * as React from "react"
import { View, type ViewProps } from "react-native"
import type { RenderProp } from "@/components/primitives/types"
import { useRender } from "@/components/primitives/use-render"
import { IconClassContext } from "@/components/ui/icon"
import { Text, TextClassContext } from "@/components/ui/text"

type MarkerVariant = "default" | "separator" | "border"

const MarkerContext = React.createContext<MarkerVariant>("default")

const markerVariants = cva("min-h-4 w-full flex-row items-center gap-2", {
  variants: {
    variant: {
stylesheet/components/ui/marker.tsx
import * as React from "react"
import { StyleSheet, View, type ViewProps } from "react-native"
import type { RenderProp } from "@/components/primitives/types"
import { useRender } from "@/components/primitives/use-render"
import { IconSizeContext } from "@/components/ui/icon"
import { Text, TextStyleContext } from "@/components/ui/text"
import { fontSize, useTheme } from "@/components/ui/theme"

type MarkerVariant = "default" | "separator" | "border"

const MarkerContext = React.createContext<MarkerVariant>("default")

const styles = StyleSheet.create({
  root: {
    minHeight: 16,
    width: "100%",