Skip to content

3.0.0

Latest

Choose a tag to compare

@gfazioli gfazioli released this 18 Aug 12:11
· 2 commits to master since this release

✨ Features

  • variant="draw" + progress — draws that share of the real perimeter, corners included, so 50% is exactly half the way around whatever the aspect ratio is. withTrack and trackColor render the rest of the perimeter underneath, the way a progress bar shows its remaining part. Changing progress transitions on its own.
  • variant="dash" — a dashed border that marches around the perimeter. dashSize, dashGap and count are percentages of the perimeter rather than pixels, so the pattern stays even at any size; dashCap="round" with a small dashSize gives dots.
  • beamMode="comet" — a stroked head with a fading tail, at constant speed on any shape. wedge cannot keep a constant speed because its sweep is angular, and dot has no trail; tail sets the length as a percentage of the perimeter.
  • triggerhover, focus-within, inView or never start the animation on interaction instead of looping forever. focus-within follows the focus of the control inside the wrapper, which is the answer for form fields: no state to wire up, and it keeps working with the keyboard.
  • offset — pushes the ring away from the content and grows its radius by the same amount, so it stays concentric instead of looking pinched at the corners.

All three stroke-based additions come from one primitive: a <rect> with pathLength="100", which turns stroke-dasharray units into perimeter percentages. It was gated on Chrome 151, Firefox 148 and Safari 26.6 before the API was designed around it — 16 rendering checks, identical on all three — so there is no ResizeObserver and no JavaScript per frame.

🐛 Bug Fixes

  • The glow was invisible inside a Card, a Paper, or any container with a background. Its z-index: -1 escaped the component and painted behind the background of the first ancestor that had one. The root now creates a stacking context.
  • borderWidth broke the pulse border instead of thickening it. Above 1px the gradient covered only a hairline of the masked band and the corners were left empty, so a wide pulse border was four disconnected lines.
  • The zIndex prop was silently ignored on the glow variant — a CSS rule beat it on specificity.
  • The dot beam now rides the middle of the border band, not its outer edge, so a small size with a wide borderWidth is no longer half clipped away.
  • progress was clamped only for the draw variant, so a negative value reached offset-distance and the browser dropped the declaration.
  • trigger="never" hid the border instead of rendering it at rest, which made it a slower way to write show={false}.

⚡ Improvements

  • The reduced-motion guard honours theme.respectReducedMotion instead of overriding it, and still reaches the pseudo-elements that Mantine's own rule cannot.
  • The decorative ring carries aria-hidden, and the accessibility suite now runs axe over both ring implementations.
  • State triggers reveal in at most 0.4s regardless of duration, so a slow loop no longer takes five seconds to appear.
  • Dash patterns are snapped to a period that divides the perimeter: otherwise the last dash was cut in half exactly where the perimeter closes, which read as a snag at the first corner.
  • variant finally appears in the Props table — mantine-docgen-script excludes factory-payload props upstream, so scripts/docgen.ts injects it.
  • Every <rect> carries width/height attributes as well as the CSS geometry properties, so an engine without SVG 2 geometry support degrades to a blunter ring instead of drawing nothing.

💥 Breaking Changes

v2 v3 Why
angle (0-360) progress (0-100) One value for "where along the border are we", shared by the static position, the draw variant and the triggers. Divide the old value by 3.6.
delay phase The value was always emitted negated: it shifted the animation, it never delayed it.
size (conic) spread size meant pixels in one beam mode and a share of the circle in the other. spread is the wedge width in real degrees.
beamMode="path" beamMode="dot" Both old names described a path around the perimeter; the shapes are what differ.
beamMode="conic" beamMode="wedge"
withMask on glow It used to be silently ignored there. It now works, and defaults to false on glow so the halo keeps its v2 look.

Passing a v2 prop logs a one-time warning in development. The Upgrade guide has the full table, before/after snippets, and the rendering changes that need no code change but do change pixels.

📝 Summary

Five variants, three beam modes, and a border that can measure a value or wait for an interaction — plus the two bugs that a careful look at the published CSS turned up. Renaming four props is the price; a find-and-replace and the upgrade guide cover it.


What's Changed

  • chore(lint): adopt oxc-config-mantine for the oxlint ruleset by @gfazioli in #25
  • chore(security): patch vulnerable transitive dependencies by @gfazioli in #26
  • chore(release): derive the repo slug from the git remote by @gfazioli in #27
  • chore(lint): bump stylelint to 17 without rewriting published CSS by @gfazioli in #28
  • v3: draw, dash and comet variants, triggers, offset, and two rendering fixes by @gfazioli in #29

Full Changelog: 2.0.9...3.0.0