Skip to content

Bump petal_components from 3.0.1 to 4.8.1 - #5020

Open
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/hex/petal_components-4.8.0
Open

Bump petal_components from 3.0.1 to 4.8.1#5020
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/hex/petal_components-4.8.0

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Jul 27, 2026

Copy link
Copy Markdown
Contributor

Bumps petal_components from 3.0.1 to 4.8.1.

Release notes

Sourced from petal_components's releases.

v3.2.2

Bug fixes

  • Menu submenu keyboard focus - when a menu toggle is activated with Space or Enter, focus now moves to the first link inside the expanded submenu instead of staying on the toggle button. Matches the WAI-ARIA disclosure menu pattern. Applies to both Alpine.js and Phoenix.LiveView.JS variants.
  • Time input icon overlap on Firefox (#377) - Firefox's native time picker icon was overlapping the AM/PM text in <.field type="time">. Time inputs now get extra right padding under Firefox to make room for the native picker indicator.
  • White button dark mode focus contrast (#315) - the white, white-shadow, and pure-white button variants had no visible focus state in dark mode. Focus now shifts the background to gray-200, matching the dark hover state.

Closes #377, #315.

v3.2.1

Bug fixes

  • Fixed disabled checkbox, switch, and checkbox-group fields still submitting a value via their hidden companion input (#483).

Native HTML form behavior excludes disabled fields from submission. Previously the hidden companion <input type="hidden"> used to default unchecked checkboxes to "false" was always rendered, so disabled fields still showed up in the form payload. It is now omitted when disabled is set.

Closes #483.

v3.2.0

Composable JS Hooks (#239)

Components now accept Phoenix.LiveView.JS struct attrs so you can compose additional actions alongside built-in component behavior:

<.dropdown on_close={JS.push("dropdown_closed")} />
<.modal on_open={JS.push("modal_opened")} />
<.alert on_dismiss={JS.push("alert_dismissed")} color="info" label="Dismissable!" />

New attrs

Component Attrs
Modal on_open
Slide Over on_open, on_close
Dropdown on_close (LiveView.JS only)
Accordion on_toggle (LiveView.JS only)
Menu on_toggle (LiveView.JS only)
Alert on_dismiss (with built-in hide behavior)
Tabs on_change
  • Added compose_js/2 helper to PetalComponents.Helpers for combining JS structs

v3.1.0

  • Added smooth transitions and easing to interactive components (buttons, tabs, accordion, dropdown, table rows, pagination, menu items, checkboxes, file inputs, alert dismiss)
  • Added focus-visible ring to buttons for keyboard accessibility
  • Added active press scale feedback on buttons
  • Fixed dropdown not working with Alpine.js 3.13.6+ (#379) — removed conflicting @click.outside on button, added initial display: none style for proper x-show initialization
  • Fixed accordion state being wiped on LiveView DOM patches (#381, #323) — added phx-update="ignore" to LV.JS accordion container, rewrote Alpine accordion to use direct active state instead of nested getter/setter pattern that broke with Alpine 3.13+
  • Added multiple attribute to accordion for expanding multiple sections simultaneously (#38) — works with both Alpine.js and LiveView.JS

... (truncated)

Changelog

Sourced from petal_components's changelog.

4.8.1 - 2026-07-28

Fixed

  • toast - timers no longer wedge on touch devices after dismissing via the X. Touch taps synthesize a compatibility mouseenter but never the matching mouseleave (the emulated pointer only moves on the next tap), so one tap inside the stack latched hover-to-pause permanently: every later toast mounted unarmed with its progress bar frozen at 100%, never auto-dismissing until a page refresh. Hover-to-pause is now wired through pointerenter/pointerleave and applies to real mice only; touch gets the natural counterpart instead - press and hold a toast to pause its timers, release to resume (which also stops a mid-drag expiry yanking a toast out from under a swipe, on any pointer). Desktop behaviour is unchanged.

  • toast - a second toast_group on the page no longer duplicates every toast. Toast delivery is global (LiveView fans push_event to every mounted hook; the petal:toast CustomEvent reaches all of them), so two mounted groups each rendered an identical toast at the same position - a burst of 6 dismissed as 12. The first-mounted group now owns global events; extra groups stay inert and console.warn naming both ids. The internal Showcase.Toast example also no longer renders its own group (hosts provide the single layout group, per the documented contract). Decided 2026-07-28 with Nic: this rides the next feature release rather than a 4.8.1 - no normal 4.8.0 usage hits it, since it requires mounting two groups against the documented one-group contract.

Internal

  • First JS test harness (vitest + jsdom, test/js/, separate js CI job). Dev-only: the Hex package still ships raw assets/js/petal_components.js and none of the harness; hooks remain plain vanilla JS with no build step. Scope rule: specs are added for behaviour that can only fail at runtime (like the toast ownership election), not as a coverage program.

4.8.0 - 2026-07-28

Added

  • color_scheme_switch - light / dark / system, in three faces. variant="toggle" flips light/dark instantly with the rotating sun/moon; variant="dropdown" opens Light / Dark / System from the same compact trigger (icon-only rail by default; labels adds text) and dismisses on select, like every menu should; variant="segmented" is the three-way pill with every state visible. All three share one no-flash contract: render <.color_scheme_script /> once in your layout's <head> and it applies the scheme before first paint, follows the OS live while the preference is system (matchMedia), keeps every open tab in sync (storage events), and dispatches petal:scheme-changed with {preference, resolved} for your own code. Stores explicit choices in localStorage.scheme - drop-in compatible with existing petal apps, and safe where storage is blocked (private browsing, sandboxed iframes). Default icons are Heroicons; light_icon / dark_icon / system_icon slots take any svg - it inherits the sizing and the toggle's rotate transition (which honours prefers-reduced-motion). Requires the PetalColorScheme hook from the bundle.

  • toast - notifications with the modern interaction grammar, LiveView-native. One <.toast_group flash={@flash} /> in the root layout. The client grammar is the full modern set: a collapsed stack (newest on top, older peeking behind at decreasing scale) that expands on hover, per-toast timeout with a progress bar, every timer pausing while you read, swipe/drag to dismiss, six positions, a queue past max, and reduced-motion support. The LiveView-native parts are the differentiators: Toast.send_toast(socket, :success, title: ...) pushes from the server; pushing again with the same id updates the toast in place - the loading -> success morph for async jobs; action: %{label: "Undo", event: ..., value: ...} renders a button that pushes a real event back to your LiveView; and anything set with put_flash renders as a toast and clears itself, so controllers and redirects get modern notifications for free. Plain JS can toast too via a petal:toast CustomEvent. The retraction half is Toast.dismiss_toast(socket, id | :all) - dismiss a loading toast when the job cancels, or clear the stack on logout - and every dismissal (timer, swipe, close, programmatic) bubbles a petal:toast-dismissed event with {id, kind}. Danger toasts announce as role="alert", the rest as polite status. Floating-panel material, kind-coloured icons and progress, zero dependencies.

  • carousel - ported home from petal_marketing. The battle-tested carousel (fade and scroll-snap slide transitions, touch swipe and mouse-drag physics, keyboard navigation, autoplay with interaction pause, bar/dot indicators, overlay/below/outside/none button placements ('outside' flanks the frame along the travel axis - left/right when horizontal, above/below when vertical), aspect sizing (video/photo/square - height derives from width, so a narrow wrapper gives the compact product-card look, with thumbnails matching the ratio), indicator_position="below" for page-coloured dots under the frame, multi-slide gallery views with edge gradients, vertical orientation, clickable slides, loop control, screen-reader announcements) joins the free library as PetalComponents.Carousel with the PetalCarousel hook. Zero JavaScript dependencies. It had been documented behind a Pro badge on petal.build since 2025 without shipping anywhere - it now actually exists where the docs said it did. Interaction logic is unchanged from the extensively tested original. New in the port: synced thumbnails (thumbnails - a strip below the frame, auto-derived from slide images with numbered chips for imageless slides; click to jump, active thumb follows, riding the same sync point as the indicators), slide corners derive from the --pc-radius token by default - amplified so the dial's steps register at surface scale and clamped at 24px so a full-radius theme gets a media-card corner, never a stadium (rounded="none" opts out, or pin a size), and every slide change dispatches a bubbling petal:carousel-change CustomEvent with {id, index, count}. Also fixed a latent bug the original never hit: controls living outside the phx-update="ignore" frame (thumbnails, below/sides buttons) now shield themselves from LiveView patches - previously any re-render of the parent LiveView would desync their state and silently detach their listeners. The material pass brings it onto the design system: overlay controls are true glass (backdrop blur, edge ring, deliberately light in both schemes - they float on imagery, not the page), below/sides buttons use the ghost material, indicators go rounded with focus-visible rings, edge gradients match the shipped dark surface, and the W3C behaviours land - autoplay respects prefers-reduced-motion, pauses on hover, and silences its live region while auto-advancing so screen readers aren't spammed.

  • local_time - timestamps in the visitor's own timezone and language. The server renders a semantic <time datetime> carrying the UTC instant (accepts a DateTime in any zone - normalised without a timezone database - a NaiveDateTime, or an ISO string); the PetalLocalTime hook formats it with the browser's Intl - no timezone tables, no date library. Presets for datetime/date/time, a map passes raw Intl.DateTimeFormat options straight through, and format="relative" gives "12 seconds ago" / "yesterday" / "in 3 weeks" (Intl.RelativeTimeFormat, numeric auto) with a decaying live tick (fresh timestamps update every few seconds, old ones hourly), a re-render when a background tab wakes so a page left open never greets you with a stale "2 minutes ago", a hover title carrying the full date, and a threshold beyond which the absolute form renders instead. locale and timezone pin specific renderings per element. The SSR fallback - and the no-JS story - is the honest UTC ISO string.

  • Showcase registry covers the 4.8 components. Carousel, Toast, ColorSchemeSwitch and Avatar join the shared example registry (shown code is the rendered code, captured at compile time), so petal.build and the playground document them from one source. Every playground page now also renders an auto-generated Properties table straight from each component's real attrs.

  • icon_placement on button. icon_placement="right" puts the icon after the label - arrows, external-link, clipboard. The loading spinner takes the icon's side, so a button doesn't reflow when it enters its loading state. Defaults to "left"; nothing changes for existing callers.

  • status on progress - the "Downloading assets..." line. A status message under the bar in the help-text tone, wrapped in a polite live region so screen readers hear stage changes without being spammed by every percent. Works with the top label row or on its own (a bare bar with a status gains the wrapper automatically). The top label row also moves to the system's 8px label rhythm - it sat 2px tighter than every form label.

  • label_placement="bottom" on stepper. The classic wizard look - circles in a row, labels centred underneath, connectors pinned to the circle centres. Horizontal only (vertical always keeps labels beside); the stacked mobile layout is unchanged. Default stays "beside".

  • random_gradient on avatar. A fancier random_color: a diagonal two-stop gradient hashed from the name, so the same person always gets the same gradient. Wins over random_color when both are set.

  • alert grows up: correct ARIA, an actions slot, custom icons. Every alert had role="dialog" - the modal-window role, flatly wrong for a message. Danger and warning now announce as role="alert" (assertive), the rest as role="status" (polite) - the same kind split the toast uses. An :actions slot renders buttons or links under the message, indented with the text column, and icon="hero-lock-closed" swaps the kind icon for any Heroicon.

  • variant="callout" on alert - the toast-cohesive form. The classic variants paint the surface in the semantic colour; the callout follows the toast's principle instead: a neutral panel surface (the floating-panel material, in flow), colour arriving as accent - a 3px left bar and a solid kind icon - with neutral text. The GitHub-markdown-alert school. This also codifies the icon rule the two components now share: tinted surfaces take outline icons, neutral surfaces take solid ones.

  • shape="rounded" on avatar and avatar_group. Proportional soft corners for orgs, teams and workspaces - circles stay the convention for people. Works across photos, monograms, art, status dots and group stacks. Deliberately independent of the --pc-radius dial: avatars are identity, not surface, so a sharp-cornered theme keeps soft avatars.

  • art on avatar - generative art placeholders. art="mesh" draws a soft multi-hue gradient orb (layered radial blobs at hashed positions), art="dither" a two-tone ordered-dither blend with solid corners and a chunky pixel band across the diagonal - the boringavatars school, natively. Deterministic per name, pure CSS plus a ~1KB inline SVG, no JavaScript, no dependencies. Pure art by default; on mesh, initials overlays a dark hue-tinted monogram (the mesh reads light, so it always contrasts). Dither stays pure art - a discrete pixel pattern never lets text sit cleanly, and it holds the bar better without. The name always labels the avatar for screen readers, and a photo src wins when present.

Changed

  • The xl progress bar's inside label stays legible at any fill. It was a single white label inside the fill, so at low percentages it sat white-on-light-track and couldn't be read until the bar grew past it (the Bootstrap-style limitation). It's now a two-layer wipe: a dark copy reads on the empty track, a light copy clipped to the filled width reads on the fill, both stacked at the same centred spot so they line up as one label that wipes colour as the bar advances. Pure CSS, clip-path animated in sync with the fill.
  • In-field actions (viewable, copyable, clearable) speak the action grammar. The eye and clipboard were solid glyphs while the clear X was a stroke; all three are stroke now - in this system solid weight marks status, stroke marks actions. Copying flips to a check in the success colour for two seconds (the GitHub convention - the old same-grey clipboard-check swap was easy to miss entirely) and announces "Copied to clipboard" through a polite live region. The password and copy buttons gain the aria-labels they never had (with aria-pressed state on the toggle), all three icons unify at gray-400 resting and brighten on hover - they never dim - and every button gets the standard focus-visible ring. copy_icon / copied_icon defaults change accordingly; overrides are untouched.
  • Radio-card selection follows the colour-as-accent doctrine: fill first, border second, dot carries the accent. Selection used to be a full-strength primary-600 border alone - on a monochrome primary that resolves to a glaring near-white frame in dark mode and a heavy near-black one in light. A selected outline card now takes a faint alpha fill (primary-600/5, dark primary-500/10 - a subtle gray step on monochrome, a soft hue glow on colour themes) with a calibrated border (primary-500, dark at 60% alpha - alpha softens a monochrome's poles more than it dulls a hue, so blue stays bold while monochrome goes REUI-restrained). And because the fill now means "selected", the outline hover no longer washes the background - hovering an unselected card firms the border instead, so hover can never impersonate selection. The classic variant is already a filled chip, so it keeps its material and speaks through the border alone.
  • Checkbox and radio labels sit tighter, top-aligned, and carry descriptions properly. The box-to-label gap tightens from 12px to 8px and the label weight steps up to medium, matching block field labels (the shadcn schooling). The control top-aligns against the first text line instead of centring against the whole block, so multi-line labels don't float the box. And a checkbox help_text now stacks in the label's text column - indented with the label, never orphaned under the box at the field's left edge. The file input's button-to-filename gap tightens to match (16px to 12px).
  • Stepper labels scale with the size dial. sm/lg used to resize only the circles while titles and descriptions stayed fixed (true since the component first shipped); now the type follows - sm tightens to caption sizes, lg steps up to base.
  • Icon spacing is part of the base button. .pc-button carries the gap itself now, so composed icons - <.button>Continue <.icon name="hero-arrow-right" class="w-4 h-4" /></.button> - space correctly with no extra attr. with_icon is a legacy no-op, kept for compatibility and removed in 5.0.

Fixed

  • input_group addon geometry: buttons, selects and kbd hints sit where the eye expects. Three drifts from 4.4: a nested <.button> had a lopsided frame (7px right, 2px above/below) and kept its full corner radius, so its curve fought the group's - it now sits as an inset chip with an even 2px frame and a concentric corner (group radius minus the frame, tracking the dial at every stop). A nested <select> kept pc-select's standalone padding - a 40px chevron zone that invisibly overlapped the field and pushed its label ~25px in - it now hugs its text with the chevron tight beside it. And a trailing <kbd> hint sat 9px from its edge while a leading icon sat 13px from its own; both ends now inset equally. The kbd chip also gained the shadcn-style micro-gap between keys: mark each modifier as its own element - <kbd><span>⌘</span>K</kbd> - and the chip spaces them 2px apart; plain text stays set solid.
  • Disabling a whole radio-card group looks disabled. disabled on the field reached every input (they were unclickable) but the grey-out style only applied to per-option disabled: true, so a group-disabled card set looked fully active. Both paths grey now. Plain checkbox and radio labels also dim with their control - the box greyed but the label text stayed full-strength.
  • Bare fields and inputs no longer require value (or label). <.field type="file" name="upload" /> - the natural way to write a file picker, which never carries a value - raised a KeyError unless you passed a pointless value=""; same for <.input>. Both now backfill a nil value, and a bare field without a label humanises one from the name, exactly as the form-field path always did. In a LiveView, that crash didn't even show on first paint: it hit on the websocket join and looped the page through endless reloads.
  • The single-thumb range is a designed slider now, and both sliders speak one language. type="range" rendered the browser's native control - default blue thumb, different geometry on every OS. It now gets the full custom treatment sharing the dual-range's exact grammar: 6px rounded track in the neutral material, 20px primary thumb with the white ring, hover grow, and the standard focus ring - identical across browsers. The dual-range came up to the current system in the same pass: its dark track sheds the old gray-700 for the gray-alpha material, its Firefox thumb finally gets a dark-mode ring (it was hardcoded white), and both sliders' focus rings move from the old paired-shade rings to the standard primary-500/50. The single track's dark colour now rides a variable set on the input element instead of a dark: utility applied to the ::-webkit-slider-runnable-track pseudo-element - the latter compiles to a trailing :where(.dark, …) that can never match a pseudo-element, so the single track had been silently staying light in dark mode. And thumbs are now the same size in every browser: the preflight border-box reset can't reach thumb pseudo-elements, and the UA defaults disagree - WebKit sizes them border-box, Firefox content-box - so Firefox drew 26px thumbs against WebKit's 20px (on the dual-range too, since it shipped). Explicit box-sizing: border-box on all four thumb rules; verified pixel-identical (14px centre + 3px ring) in headless Chrome and Firefox.
  • fill on type="range" - an optional primary fill up to the thumb. Off by default (the plain track is right for balance and pan controls, where a fill implies a wrong zero point); fill paints the track primary from the start to the thumb. Firefox fills natively via ::-moz-range-progress; webkit has no native lower-fill, so the track paints a hard-stop gradient whose split the new PetalRangeFill hook keeps in sync, with the initial position server-rendered so it is correct before the hook connects and with JS off. On <.input> and <.field>.
  • One close-button grammar across alert, modal, slide-over and toast. The four X buttons had drifted into four behaviours: the alert's oversized hit area grew headingless alerts when dismissible, the modal's X had no dark-mode styles at all (so hovering dimmed it on dark), washes and focus rings were present or absent at random. Now every X follows the same rules: a bare glyph (no hover box - a wash rectangle in a corner draws chrome where nothing else has any, and can never sit misaligned against text) that brightens on hover, a generous invisible hit area that never participates in layout, a focus-visible ring, and it never dims. The alert's glyph keeps its per-colour tinted hover.
  • alert no longer crashes when on_dismiss is nil. Conditionally passing the attr - on_dismiss={if @dismissible, do: JS.dispatch(...)} - is a natural pattern, and the nil case raised at render. Nil now behaves like the empty default: no dismiss button.
  • Textareas clamp the radius token. A full-radius theme turned multiline fields into over-rounded pills that ate their own text; textareas now cap at 1rem while single-line inputs keep the pill option - that one is a real style.

... (truncated)

Commits
  • 4919682 Merge pull request #508 from petalframework/showcase-toast-example
  • 127f1ac fix: keep the toast showcase example attached to toast_group
  • 21eee2f Merge pull request #507 from petalframework/release-4.8.1
  • 56b3329 🔖 Release 4.8.1
  • 1d5eb53 Merge pull request #506 from petalframework/toast-mobile-pause
  • 3bac62f fix: gesture ends reliably regardless of where the pointer releases
  • fc91cad fix: gesture identity + mid-drag resume guards (Greptile)
  • 4e108d1 fix: toast timers wedged on touch after dismissing via the X
  • 554b8e0 docs: Unreleased changelog section for the toast ownership fix
  • 4778b0f Merge pull request #505 from petalframework/toast-dup-fix
  • Additional commits viewable in compare view

@dependabot dependabot Bot added dependencies Pull requests that update a dependency file elixir Pull requests that update Elixir code labels Jul 27, 2026
@github-project-automation github-project-automation Bot moved this to New Issues in Core Jul 27, 2026
@github-actions

Copy link
Copy Markdown

Security Review

⚠️ Review did not complete. See the workflow run.

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.5%. Comparing base (e776f7c) to head (899cf40).

Additional details and impacted files
@@          Coverage Diff          @@
##            main   #5020   +/-   ##
=====================================
  Coverage   90.5%   90.5%           
=====================================
  Files        425     425           
  Lines      20237   20237           
=====================================
+ Hits       18310   18313    +3     
+ Misses      1927    1924    -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Bumps [petal_components](https://github.com/petalframework/petal_components) from 3.0.1 to 4.8.1.
- [Release notes](https://github.com/petalframework/petal_components/releases)
- [Changelog](https://github.com/petalframework/petal_components/blob/main/CHANGELOG.md)
- [Commits](petalframework/petal_components@v3.0.1...v4.8.1)

---
updated-dependencies:
- dependency-name: petal_components
  dependency-version: 4.8.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
@dependabot dependabot Bot changed the title Bump petal_components from 3.0.1 to 4.8.0 Bump petal_components from 3.0.1 to 4.8.1 Jul 29, 2026
@dependabot
dependabot Bot force-pushed the dependabot/hex/petal_components-4.8.0 branch from 3a0be37 to 899cf40 Compare July 29, 2026 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file elixir Pull requests that update Elixir code

Projects

Status: New Issues

Development

Successfully merging this pull request may close these issues.

0 participants