Mishka Chelekom v0.0.10 Galaxy: Phoenix, Headless and Mobile UI

57 new headless components for 92 in all, 100+ native iOS and Android components for Mob, a class attribute for every part, and UI kits that drop into Mishka CMS.

Mishka Chelekom v0.0.10 Galaxy: Phoenix, Headless and Mobile UI

Mishka Chelekom v0.0.10 is out, and we named it Galaxy. With this release Chelekom covers the whole Elixir front end: styled Phoenix LiveView components, headless components you paint with your own classes, and native iOS and Android components for Mob. Every styled and headless component also ships as a UI kit that Mishka CMS installs and draws at runtime.

To mark the release we redesigned the Mishka Chelekom landing page as a galaxy: one sky, with the styled, headless and mobile lines as its worlds. v0.0.10 reached Hex on October 25, 2026, two years to the day after 0.0.1.

The redesigned Mishka Chelekom landing page: a starfield with floating component cards around the headline Phoenix UI kit and mobile components

The new Mishka Chelekom landing page, redesigned for the Galaxy release.

100+styled components
92headless components
100+native Mob components
76,000+downloads on Hex

Here is how the four parts fit together. Each line has its own generator, and all of them come from the same component designs:

01 · Styled100+ Phoenix LiveView componentsTailwind CSS v4, colors, variants and sizes ready. mix mishka.ui.gen.component
02 · Headless92 unstyled, accessible componentsARIA, keyboard and state, painted with your classes. mix mishka.ui.gen.headless
03 · Native mobile100+ components for MobDrawn by Jetpack Compose and SwiftUI, no WebView. mix mishka.ui.gen.mob
04 · CMS kitsTwo UI kits for Mishka CMSchelekom.json and chelekom_headless.json on every release

Below, each change gets its own section with code you can copy. The complete list is in the v0.0.10 changelog and on the GitHub release page.

Get started with Mishka Chelekom v0.0.10

Add the new version to your mix.exs. Mishka Chelekom is a dev-only dependency with no runtime: the generators write plain source into your project, so nothing from Mishka runs in production.

elixir
{:mishka_chelekom, "~> 0.0.10", only: :dev, runtime: false}

Run mix deps.get, then regenerate the components you want updated. Your app does not change until you do, and Igniter shows every change as a diff before it writes. A new Phoenix project can start with Chelekom in one command:

mix igniter.new my_app --with phx.new --install mishka_chelekom

v0.0.10 needs Elixir 1.18 or newer and Tailwind CSS v4. The full guide is in the Mishka Chelekom documentation, and every task and flag is on the CLI page.

Native iOS and Android components with Mob

Chelekom's components now run on the phone. v0.0.10 brings 100+ native components to Mob, the framework that runs Elixir on iOS and Android, as widgets that Jetpack Compose and SwiftUI draw.

There is no HTML, CSS, JavaScript or WebView in them, and nothing to install from npm. Your screen keeps the state in Elixir, the components render a tree of native nodes, and taps arrive in your screen's handle_info/2. The work landed in #495.

Five Android phones showing native Mishka Chelekom components for Mob: an angle slider, an alert dialog, a color picker, a file tree and a stack of toasts

Angle slider, alert dialog, color picker, tree and toast, running natively on Android.

Three tasks generate them. Each component lands in lib/<app>/components/ and registers its tag, so you can write it in your screen's ~MOB markup right away:

mix mishka.ui.gen.mob chip
mix mishka.ui.gen.mob color_picker --module-prefix mishka_   # the tag becomes <MishkaColorPicker>
mix mishka.ui.gen.mob.components                             # all of them
mix mishka.ui.gen.mob.components chip,tabs,drawer
mix mishka.ui.uninstall drawer --mob

A screen uses them like any other Mob tag. Here two chips toggle a list of tags:

elixir
~MOB"""
<Row fill_width={true}>
  <Chip label="Elixir" checked={:elixir in @tags} on_toggle={{:tag, :elixir}} />
  <Spacer size={8} />
  <Chip label="Erlang" checked={:erlang in @tags} on_toggle={{:tag, :erlang}} />
</Row>
"""

def handle_info({:tap, {:tag, id}}, socket) do
  tags = socket.assigns.tags
  next = if id in tags, do: List.delete(tags, id), else: [id | tags]
  {:noreply, Mob.Socket.assign(socket, :tags, next)}
end

The generator declares your new tags in your own config, in config :mob, :extra_tags. It adds only the missing ones and keeps the rest of the list, and uninstall removes just the tags it took away. Earlier alphas wrote into deps/mob/priv/tags, which mix deps.get threw away (#533).

elixir
config :mob, extra_tags: ["Chip", "Tabs"]

Android is our reference platform. Every release runs 410 device tests against the showcase app on an Android emulator, and the release page ships that app as APKs, so you can try every component on your own phone. On iOS, Mob's SwiftUI renderer draws the same components; we keep the gaps we found in Mob's iOS renderer in a public list, with the fix for each.

The release also fixed a memory leak in the generated Android activity: 200 popover taps grew the heap from 14 to 51 MB before, and it stays at 6 MB now (#532). Lists, menus, tabs and underlines now draw every item at the width of their text (#528).

GenericJam builds Mob, and GenericJam's first pull request to Chelekom, #504, virtualized the showcase's home grid and cut its screen mount from about 780 ms to about 300 ms on a Moto G Power. Thank you.

Every Mob component has its own page with phone screenshots in the Mob docs, for example the chip.

The headless line grows from 35 to 92 components

v0.0.9 shipped 35 headless components ported from Base UI. v0.0.10 adds 57 more, for 92 in all. Most of the new ones are converted from Mantine, and each one ships semantic markup, WAI-ARIA wiring, named parts, data-* state and, where it needs one, a JavaScript hook, with no colors or spacing of our own.

v0.0.9: the first headless line

35 components ported from Base UI.

38 new components, most from Mantine

Tree, tags input, color picker, splitter, rolling number and the TipTap editor among them, most merged in #492. The line reaches 73.

Chart and sparkline

One chart API on three engines, and a server-rendered sparkline. 75 components.

17 more, from calendar to table

Merged in #502. The headless line reaches 92.

The headless calendar docs example: a month grid for October 2026 with today marked, above its HEEx code with Tailwind classes on each part

The new headless calendar, painted only with the classes in its example.

The 57 new headless components, by what they do:

Many of them render on the server with no JavaScript at all, such as highlight, number formatter, semi circle progress and tags input:

heex
<.semi_circle_progress value={72} label="Battery" shape="semi"
  track_class="stroke-base-300" indicator_class="stroke-primary">
  72%
</.semi_circle_progress>

<.highlight text="Phoenix LiveView" highlight="live" mark_class="bg-yellow-200" />

<.number_formatter value={1234567.891} thousand_separator="," decimal_scale={2} prefix="$" />

Two of them need an npm package, and you pick the engine with --lib. The chart runs on Apache ECharts (the default), Chart.js or billboard.js behind one API. The editor runs on TipTap 3 (the default), CodeMirror, Milkdown or Lexical, and stores JSON, HTML, text or Markdown. The generators detect bun, npm or yarn, and fall back to the bun Hex package, so you need no Node.js on your machine:

mix mishka.ui.gen.headless tree
mix mishka.ui.gen.headless chart --lib billboard
mix mishka.ui.gen.headless editor --lib tiptap
mix mishka.ui.gen.headless.components              # everything except the npm-backed ones
mix mishka.ui.gen.headless.components --with-npm   # chart and editor too

Here is the chart with its default engine. You write the engine's own option map in Elixir, and the series colors come from CSS custom properties, so a chart follows your light and dark themes:

Two headless chart examples drawn with Apache ECharts: a smooth line chart of monthly revenue in US dollars and a donut chart of traffic by source

Two examples from the headless chart docs, drawn with Apache ECharts.

heex
<.chart
  id="traffic"
  height="20rem"
  class="w-full max-w-md"
  aria_label="Traffic by source"
  option={
    %{
      tooltip: %{trigger: "item"},
      legend: %{bottom: 0},
      series: [
        %{
          name: "Source",
          type: "pie",
          radius: ["48%", "72%"],
          data: [
            %{value: 1048, name: "Search"},
            %{value: 735, name: "Direct"},
            %{value: 580, name: "Referral"},
            %{value: 484, name: "Social"},
            %{value: 300, name: "Email"}
          ]
        }
      ]
    }
  }
/>

The TipTap editor also closes issue #179, which asked for a rich text editor in 2024. Browse all 92 in the headless docs.

Paint any headless part from your markup

Every part a headless component renders now takes its own *_class attribute, so you can style any part from the markup, with no stylesheet. The headless line has 403 of these attributes across 78 templates, up from 169 across 31 in v0.0.9.

Here a switch gets a daisyUI look from its attributes, and the new :on_icon and :off_icon slots draw inside the track:

heex
<.switch id="notify" name="notify" checked
  track_class="bg-base-300 data-checked:bg-primary"
  thumb_class="bg-base-100"
  label_class="text-sm">
  <:on_icon>On</:on_icon>
  <:off_icon>Off</:off_icon>
  Email me about replies
</.switch>

To show what that makes possible, the development app in the repository has a daisyUI gallery next to the Base UI one: all 92 headless components in 504 examples, painted with daisyUI and Tailwind classes written in the examples' own markup. Copy an example and you get the component and its look. daisyUI stays your own dependency, and the same works with any design system you use.

Chelekom components, ready for Mishka CMS

Every Chelekom release now ships two UI kits for Mishka CMS: chelekom.json with the styled set and chelekom_headless.json with the headless set. Mishka CMS installs a kit straight from the GitHub release and compiles its components at runtime, for your pages, your posts and its visual editor.

1 · TagA release is taggedv0.0.10 on master
2 · ExportCI builds two kitsMarkup, attributes, hooks, CSS and icons
3 · ReleaseAttached to the releaseWith SHA256 checksums
4 · InstallMishka CMS installs itOne kit per site, from the admin
5 · LiveIn the editor and on pagesCompiled at runtime, no deploy

The kits are richer than in v0.0.9. The styled kit carries 136 components with 423 ready examples, and the headless kit 109. Each attribute now ships the options it takes, so the editor can offer a list: 418 attributes have one, and 317 of them also accept your own class string, such as size="py-1 px-2". Every component links to its docs page, the sample pictures travel inside the kit, and 1,288 Heroicons give the CMS an icon picker from the same file.

Several components used to arrive blank or wrong in a CMS, and they are fixed:

  • An alert with kind="danger" drew the neutral style (#516).
  • Text, URL, email and other field labels drew a generic gray label (#517).
  • Every headless button failed with "Component failed to render!" because of a name mismatch (#524).
  • The headless calendar and countdown drew nothing, and seven components such as the carousel and the tooltip arrived as an empty strip (#513, #506).

You can build a kit from your own components too. The export task walks a folder and writes a kit Mishka CMS can install:

mix mishka.ui.export priv/components --cms --name chelekom --bundle-name chelekom --bundle-version 0.0.10 --yes
mix mishka.ui.export priv/headless --cms --name chelekom_headless --bundle-name chelekom_headless --bundle-version 0.0.10 --yes

This blog runs on Mishka CMS, and the timeline, the notes and the questions in this post are Chelekom components from those kits. Read how kits work in components and UI kits, how to add one in install or upgrade a UI kit, and the story of the CMS in Introducing Mishka CMS.

Your theme tokens stay in place (issue #511)

Generating components no longer overwrites your Tailwind @theme block. Chelekom now sets only its own tokens and keeps yours, and uninstall takes back only what install added.

tanguilp reported it in issue #511: after mix mishka.ui.gen.component tooltip, the project's own --font-caveat and --color-brand were gone, because the generator replaced the whole body of the block. A project with @theme inline even got all 251 of Chelekom's tokens written into it. With igniter_css 1.1, an existing token only changes its value, a missing one is added, and your tokens and comments stay where they are:

css
@theme {
  --font-caveat: "Caveat", cursive;               /* yours, kept */
  --color-brand: #0f766e;                         /* yours, kept */
  --color-primary-light: var(--primary-light);    /* Chelekom's, set or updated */
}

A @theme inline block is left alone. Generating and then uninstalling now returns app.css byte for byte, which we checked in a fresh Phoenix 1.8 app:

mix mishka.ui.gen.components --import --helpers --global --yes   # a second run changes nothing
mix mishka.ui.uninstall --all --include-css --include-config      # app.css comes back as it was

A css_overrides value that is not valid CSS now stops generation and names the override, where it used to ship the default silently. Thank you, tanguilp, for the clear report. The fix is #512.

Floating menus open in the right place, and much faster

Dropdowns, popovers and tooltips share one JavaScript hook, and two of its bugs are gone. A hover menu could open at the far left edge of the page instead of under its trigger, and a page with many tooltips froze for seconds after a LiveView navigation.

The menu opened in the wrong place because the hook measured its content before taking it out of the page flow, when it was as wide as the page (#522). The freeze came from reading the layout while mounting. Now the hook reads no layout until a menu opens (#520). We measured it on our design system page, which has 142 floating hooks:

Mounting every hook

before
2,788 ms
after
1 ms

From a click to the new page title

before
2,929 ms
after
102 ms
Measured in headless Chrome for #520. Before the fix, mounting took 2,788 to 4,731 ms and a click took up to 5,027 ms.

You get the fix by regenerating the floating components. Nothing changes in how you write them:

heex
<.dropdown id="user-menu" clickable={false} smart_position position="bottom" relative="relative">
  <:trigger>
    <.button>Account</.button>
  </:trigger>
  <:content>
    <p>Signed in as Shahryar</p>
  </:content>
</.dropdown>

See the dropdown, popover and tooltip docs.

Accessibility fixes: alt text and names for icon-only controls

An audit of our own docs found images and icon-only buttons that callers had no way to name. They all take a name now (#523):

  • Carousel: each slide takes alt, where an empty string marks it decorative, and the previous and next buttons have labels you can change.
  • Accordion and timeline: their images take alt. The timeline used the image's file path as its alt text before.
  • Password field: the show and hide toggle has a label and aria-pressed. The search field's button is labelled Search.
  • Dock and speed dial: a dock with hidden labels keeps them for screen readers, and each speed dial item takes a label.
heex
<.carousel id="hero" indicator previous_label="Previous photo" next_label="Next photo">
  <:slide image="/images/1.webp" alt="Aurora over a lake" title="North" />
  <:slide image="/images/2.webp" alt="" />
</.carousel>

<.speed_dial id="actions" icon="hero-plus" clickable>
  <:item icon="hero-home" label="Home" href="/"></:item>
</.speed_dial>

See the carousel and speed dial docs.

The editor and the chart work under a strict CSP

Under a Content Security Policy that allows no inline style, the headless editor and chart now keep their look and log no violations. Both used to inject <style> elements that such a policy blocks.

TipTap 3 adds its own stylesheet by default. The TipTap engine now turns that off, and the few base rules ProseMirror needs are Tailwind classes on the editor surface. Bold also shows as active at once at a collapsed cursor, where it used to lag one keystroke (#525). If you want TipTap's stylesheet back under a nonce, turn it on in the extensions file the generator creates for you:

js
// assets/vendor/editor_extensions.js
export default {
  extensions: [],
  starterKit: {},
  injectCSS: true,
  injectNonce: document.querySelector('meta[name="csp-nonce"]')?.content,
};

With billboard.js, the chart filled its lines in black and lost its axis styles under such a policy. It now adds billboard's rules once per page as an adopted stylesheet, which no style-src governs, and adopts it again after a LiveView navigation. A new test fails on any hook in the library or the CMS kits that writes a <style> element or a style attribute.

Rating fills the stars you asked for

A styled rating given its value as text now fills the right number of stars. select="3" filled all five, because in Elixir 1 <= "3" is true, and that is exactly how a value arrives from a HEEx string or from CMS content. The empty stars also keep their gray now (#518).

heex
<.rating select="3" />                    <%# 3 stars; it filled 5 before %>
<.rating select="2.5" precision={0.5} />  <%# 2 full and a half; it filled 5 before %>
<.rating field={@form[:rating]} precision={0.5} color="warning" />

The headless rating reads a whole star to screen readers as "3 of 5" instead of "3.0 of 5". See the rating docs.

Tailwind CSS v4: class names, pointer cursors and borders

Every class in the templates, demos, usage rules and hooks now uses Tailwind CSS v4's names. v4 kept most v3 names as aliases, so the kit compiled fine, but some classes drew something other than what they were written for. We compiled every class with Tailwind 4.3.3 and fixed what we found:

  • Renamed scales: shadow-sm is shadow-xs, rounded-sm is rounded-xs, and bg-gradient-to-* is bg-linear-to-* in 371 gradient classes. The sizes you see do not change.
  • Buttons show the pointer again. v3's preflight gave every button cursor: pointer, and v4's does not. 31 clickable elements in the templates now carry cursor-pointer themselves, and so do the examples in the docs.
  • Borders have their own color. v4 draws a bare border in the text color, where v3 drew gray. Accordions, button groups, the combobox trigger and others now name a border color.
  • 40 classes that drew nothing, such as typos and theme colors the theme never defined, are fixed.

One change is visible. In v4, rounded-sm and a bare rounded are the same size, so extra_small and small looked identical in 50 components. extra_small is its own size again (#526):

heex
<.badge rounded="extra_small">2px corners</.badge>   <%# it was 4px, the same as small %>
<.badge rounded="small">4px corners</.badge>

Tests now fail on a v3 class name, on a button without the pointer and on a border without a color. Chelekom supports Tailwind CSS v4 only. The tokens and sizes are on the design system page.

Component fixes found at phone width

We checked every docs example at phone width and rendered every component with only its required attributes. These fixes came out of it (#524, #526):

  • Your display classes win. A button's own inline-flex beat your hidden, so class="hidden md:flex" never hid it. A component now drops its own display class when yours names one, in 26 templates.
  • Form fields render without a value. <.email_field name="x" /> raised KeyError. name and value default to nil on every field, and a bound toggle or color field keeps the field's value.
  • Banner positions name their edge. border_position="top" drew the bottom border. A banner with neither attribute looks as before.
  • Avatars and breadcrumbs. An avatar keeps its initials in a narrow row, and a long breadcrumb wraps instead of overlapping.
  • rounded="none" means no radius on 14 components, where it printed a class called none.
  • Three crashes are gone: a carousel slide without an image, a range field value without a position, and a combobox without an id.
heex
<.button class="hidden md:flex">Desktop only</.button>

<.banner id="promo" border_position="top" rounded_position="top" rounded="large">
  The border and the rounded corners are on top now.
</.banner>

<.email_field name="email" />

Generated code formats the same on every Elixir version

A component generated on one Elixir version used to fail mix format --check-formatted on another. 31 helper clauses were written as one-line guards, and once such a clause grows past the line width, Elixir 1.18 and 1.19 lay it out differently. They are written with do ... end now, which every version formats alike:

elixir
# before: Elixir 1.18 and 1.19 format this differently once it outgrows the line
defp size_class(size) when is_binary(size) and size != "", do: "p-#{size}"

# after: the same on every version
defp size_class(size) when is_binary(size) and size != "" do
  "p-#{size}"
end

A test fails on any new clause written the old way. The generated components also pass mix credo --strict and compile without warnings on Elixir 1.20.

Docs badges come from each component's options file

The badges on a docs page, such as Stateless, JS and Accessible, are now declared by each component instead of guessed from its template. Every component's options file now ends with a docs block, and the docs site reads it (#515):

elixir
docs: [stateful: false, js_module: true, js: false, accessible: false]

The generators ignore the block, so it changes nothing in your project. Along the way, 28 headless docs links that used underscores and answered 404 now use the docs site's dashed addresses (#521).

Security advisories patched in our lockfiles

The release pipeline now audits dependencies, and it stopped an alpha tag on real advisories. We updated mint, html_sanitize_ex, bandit and plug_crypto in the repository's lockfiles, including two advisories rated high (#500, #499, #529).

Chelekom is a dev-only dependency and our lockfile does not reach your app, so your own lockfile is the one to check. Both audits take a minute to add:

elixir
{:mix_audit, "~> 2.1", only: [:dev, :test], runtime: false}
mix hex.audit && mix deps.audit

In our run, mix hex.audit caught four advisories that mix deps.audit did not report, so we run both. More on how we handle security is on the security page.

One tag publishes the whole release

v0.0.10 is the first Chelekom release built entirely by CI. Pushing a version tag now runs 14 jobs: the library and generator tests, a security scan, the dependency audit, both CMS kits, the Hex package, the Mob tests and the Android builds. Then it publishes everything to the GitHub release with a checksum file.

The workflow refuses a tag that disagrees with the version in mix.exs, and alpha, beta and rc tags publish as pre-releases. You can check any download against its checksum:

gh release download v0.0.10 -R mishka-group/mishka_chelekom -p chelekom.json -p SHA256SUMS.txt
sha256sum -c --ignore-missing SHA256SUMS.txt

Running the Android app in CI also found three real bugs in it, code that had never run before, and they are fixed (#499).

Other fixes

  • The file upload dropzone opens the file picker again. Its label pointed at an id the input never had. Thank you to Rope-a-dope for the report (#496).
  • The first JavaScript hook in a project registers. A project whose only hook was the headless chart never mounted it.
  • The headless radio group can be horizontal, with an orientation attribute the arrow keys follow.
  • A headless navigation menu item with an href is a link, not a dropdown.
  • The chart docs name the right flag, --lib, where they said --engine.
  • The usage rules show the usage_rules 1.2 setup for AI agents.

Before you upgrade

Components are generated into your project, so nothing changes until you regenerate. When you do, check these:

For Mob apps, regenerating writes your tags to config :mob, :extra_tags, and a few components changed their layout, such as the drawer trigger and the overflow list counter. The changelog lists each one.

What comes next

Our goal is still a stable v0.1.0. With the styled, headless and mobile lines in place, the next releases harden them and fix what you report on GitHub. The roadmap has the details.

Mishka Chelekom is young and covers a lot of ground, so you will find bugs. Please open an issue on GitHub when you do; reports like #496 and #511 made this release better.

Support Mishka Chelekom

Mishka Chelekom is free and open source under the Apache 2.0 license, and a small team builds it. If it saves you time, a sponsorship of any size keeps the releases coming.

If your company needs components of its own, a design system built on Chelekom, or help with a Phoenix LiveView project, look at our custom services or hire our team.

Questions people ask about Mishka Chelekom v0.0.10

Native iOS and Android components for Mob, 57 new headless components for 92 in all, a class attribute for every headless part, and UI kits that Mishka CMS installs from each GitHub release.

Yes. The mix mishka.ui.gen.mob task writes native components for Mob, the framework that runs Elixir on iOS and Android. Jetpack Compose and SwiftUI draw them, with no HTML or WebView.

Yes. Every part of a headless component takes its own class attribute, so you can paint it with daisyUI or plain Tailwind classes in your markup. daisyUI stays your own dependency.

Set the version to 0.0.10 in mix.exs as a dev-only dependency, run mix deps.get, and regenerate the components you want updated. Nothing in your app changes until you regenerate.

Yes. Each release ships two UI kits, chelekom.json and chelekom_headless.json. Mishka CMS installs them and draws their components at runtime in pages, posts and its visual editor.

Yes. It is open source under the Apache 2.0 license. The generators write plain source into your project, so nothing from Mishka runs in production.

Thank you

Thank you to GenericJam for Mob and for a first pull request to Chelekom, and to tanguilp and Rope-a-dope for the reports that made this release better. Thank you to the teams behind Base UI, Mantine, daisyUI and TipTap, whose work these components learn from.

And thank you to Zach Daniel for Igniter, the base of every Chelekom generator, and to everyone on our sponsor page who keeps the project going.

The Mishka team

Contributors Mishka Group Core
Shahryar Tavakkoli
LEAD
Shahryar Tavakkoli @shahryar-tavakkoli
Elixir · Phoenix · LiveView

Shahryar Tavakkoli writes about Elixir and Phoenix LiveView: Mishka Chelekom releases, GuardedStruct validation, static sites, service workers and SEO.

Comments

Sign in to comment

No comments yet.