Separator

v0.0.10

v0.0.10StableMobStatelessApache-2.0
Installation mix mishka.ui.gen.mob separator
Source View Source

The Mishka Chelekom Mob Separator is a rule for iOS and Android apps built with Mob. It divides groups of content with a horizontal or vertical line, optionally carrying a centered label, and it is the native port of the headless Separator.

Overview of the Mob Separator component for iOS and Android

A horizontal separator is a single line. With a label it becomes a line, the label and a second line, and the two lines share the leftover width so the label stays centered. A vertical separator sits between items in a Row. It needs a parent that gives it height, or an explicit length.

Style the rule with color, which takes a theme token or an ARGB integer and defaults to :border, and with thickness, which defaults to 1 dp or pt. On a labeled rule, space sets the gap between the label and the lines, and label_size, label_color and label_weight style the label.

By default a horizontal rule is Mob's <Divider> node. Set render={:box} to draw it as a filled Box instead. The two sections after the examples explain when to do that.

A separator draws a line and sends no events. It holds no text or state, so a device test has nothing to query. Give it an id to tag it. A labeled separator also tags its two lines <id>-line-start and <id>-line-end, which lets a test check that the label sits between two equal lines.

Generate the separator component

Run this from your Mob app's root to add the component to lib/<app>/components/:

bash
mix mishka.ui.gen.mob separator

The generator registers the <Separator> tag so you can use it in ~MOB markup. The examples on this page use the mishka_ prefix of the showcase app (--module-prefix mishka_), so the tag is <MishkaSeparator>. Without a prefix the tag is <Separator>.

Plain separator

A bare MishkaSeparator draws a horizontal rule. Here it divides two blocks of text.

Plain separator

Plain rule card with the text Section one, a thin horizontal rule and the text Section two, in the MishkaMob showcase app on Android
heex
<Column fill_width={true}>
  <Text text="Section one" />
  <MishkaSeparator />
  <Text text="Section two" />
</Column>

Labeled separator

Set label to draw a line, the label and a second line, with the label centered between them. space sets the gap between the label and the lines, which is 12 by default. The screenshot also shows a separator labeled 1994. On iOS, this form needs render={:box}. The section on separators on iOS explains why.

Labeled separator

Labelled card with two horizontal rules, one with the centered label or continue with and one labeled 1994, in the MishkaMob showcase app on Android
heex
<MishkaSeparator label="or continue with" />

Separator color and thickness

color takes a theme token or an ARGB integer, and thickness sets how thick the rule is, in dp or pt. This rule is violet with a thickness of 3. The screenshot also shows a rule in the primary color at thickness 2 and a muted labeled rule at thickness 2.

Separator color and thickness

Colour and thickness card with a thick violet rule, a thick near black rule and a gray rule labeled muted + thick, in the MishkaMob showcase app on Android
heex
<MishkaSeparator color={0xFF7C3AED} thickness={3} />

Vertical separator

Set orientation to :vertical to draw a rule between items in a Row. A vertical rule needs a parent that gives it height. The code here shows the Row alone, and the showcase screen puts it in a Box with a height of 28. Set length to give the rule its own height. On iOS, a vertical rule needs length. The section on separators on iOS explains why.

Vertical separator

Vertical card with Docs, Guides and API in a row, divided by a thin gray vertical rule and a thicker near black one, in the MishkaMob showcase app on Android
heex
<Row>
  <Text text="Left" />
  <MishkaSeparator orientation={:vertical} />
  <Text text="Right" />
</Row>

Draw the separator with render box

On Android, <Divider> is Material 3's HorizontalDivider, which draws an antialiased stroke instead of a filled rectangle. At a screen density of 2.6875, a 1 dp rule gets a canvas 3 pixels tall and a stroke 2.6875 pixels wide. The bottom pixel row ends up about 69% covered, so it is slightly lighter than the two rows above it. No mix of color and thickness gives a crisp one-pixel line, because the softness comes from the primitive.

Set render={:box} to draw a background-filled Box, a rectangle with no antialiased edge, so every pixel row has the full color. It applies to the plain rule and to both lines of a labeled rule. A vertical rule is a Box either way and ignores render.

heex
<MishkaSeparator render={:box} />
<MishkaSeparator render={:box} label="or continue with" />
  • A :box rule reads a shade heavier. At the same thickness the divider paints two full rows and one row at about 69%, and the box paints three full rows.
  • A horizontal divider and a vertical rule that share one color and one thickness render as two slightly different grays, because the vertical rule is already a Box. render={:box} makes them agree.
  • The default stays :divider, because the plain rule works on iOS with it.

Use separators on iOS

  • The plain horizontal rule works.
  • The vertical rule renders 1 pt wide and 0 pt tall, so it is invisible. Set length. A rule with a length carries both a width and a height, and iOS sizes it.
  • The two lines of a labeled rule come out as 1 pt by 1 pt ticks. SwiftUI's Divider draws along the axis of its container, so inside the Row that centers the label it becomes a vertical hairline. render={:box} fixes this, because a Box has no opinion about its container's axis.
  • The iOS renderer does not read weight, so the two lines cannot share the leftover width that way. render={:box} avoids the problem. This comes from reading the renderer code and is not confirmed on a device.
heex
<MishkaSeparator orientation={:vertical} length={24} />
<MishkaSeparator label="or continue with" render={:box} />

Props

PropTypeDefaultDescription
orientation:horizontal or :vertical:horizontalRule axis. A vertical rule needs a parent that gives it height, such as a Row, unless length is set.
labelstringnilDraws a line, the label and a second line. Horizontal only.
colorcolor token or ARGB integer:borderRule color.
thicknessnumber1Rule thickness in dp or pt.
spacenumber12Gap between the label and the lines.
render:divider or :box:dividerWhich primitive draws a horizontal rule: a Material divider, which is a stroke, or a filled Box, which is a rectangle. A vertical rule ignores it.
lengthnumbernilExplicit length of a vertical rule in dp or pt. nil fills the height of the parent.
label_sizetext size token or number:smLabel text size.
label_colorcolor token or ARGB integer:mutedLabel color.
label_weight:bold, :semibold, :medium, :light, :thin or :regularnilLabel font weight. The component sends it as font_weight, never as weight, because a bare weight on a Text is a layout weight to the parent Row. nil omits the prop.
idstringnilTest tag. A labeled rule also tags its lines <id>-line-start and <id>-line-end.

Differences from the web component

  • decorative is not ported. On the web it only changes role="separator" to role="none" for screen readers, and Mob exposes no such role on a divider.
  • The headless class and label_class attributes do not exist here. The props color, thickness, label_color, label_size and label_weight style the rule and its label.