Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message
kinetix-alerts
happones/kinetix
claude-code
alerts
dismissals
kinetix-alert
flash-messages
transitions
accessibility

In-page status alerts (<KinetixAlert>) and server-flashed alerts (KinetixFlash::alert + <KinetixFlashAlerts>, one-shot / keep / untilDismissed): success/danger/warning/info/primary/gray on soft/outline/accent surfaces, close modes (hide/session/device/permanent, timed, "don't show again"), named transition presets that stop under reduced motion, and screen-reader/colorblind-safe semantics. Activates when showing an inline notice, callout, warning box or dismissible banner inside a page.

Install
php artisan boost:add-skill happones/kinetix

Save this content to: .claude/skills/kinetix-alerts/SKILL.md

# Kinetix Alerts Development

## When to Apply

Activate this skill when:
- Showing an inline notice, callout, warning or error box inside a page.
- Making a notice closable for this page, this tab, this browser, or for good.
- Animating something that appears/disappears (reuse the transition presets).

For transient feedback after an action use a toast (`KinetixFlash::success()`
or `Notification::make()`), not an alert. For product news use announcements.

## Documentation

For full details, reference `docs/alerts.md` (published at https://happones.github.io/kinetix/alerts).

## The component

```vue
<KinetixAlert
    color="warning"
    variant="soft"
    :title="t('billing.trial_ending_title')"
    :description="t('billing.trial_ending_body')"
    dismissible
    dismiss-mode="session"
    dismiss-key="trial-ending"
    dont-show-again
    :persist="saveDismissal"
    transition="collapse"
>
    <template #actions>…</template>
</KinetixAlert>
```

`color`: success · danger · warning · info · primary · gray. `variant`: soft ·
outline · accent. `dismiss-mode`: hide · session · device · permanent
(`persist` = `(key) => Promise` stores a permanent close). `transition`: fade ·
slide-down · slide-up · scale · collapse · none.

- `soft` / `outline` / `accent`: the color goes on the surface and the icon
  only; title and body stay on the foreground tokens. `solid`: the fill sets
  its own text color (`-foreground`; danger = `text-white` +
  `dark:bg-destructive/60`), and the icon, text and close button inherit it
  (`text-current`, button variant `ghost-current`). Surface classes come from
  `statusAlertClass(color, variant)` in `useKinetixStatusColor`. The banner
  takes no `solid` (its carousel controls are drawn for light surfaces).
- Contrast is guarded by `tests/js/statusTokenContrast.spec.ts`, which parses
  `kinetix.css` and checks text-on-page, text-on-tint, solid fill and the
  white-on-red recipe at 4.5:1 in BOTH themes. Re-tuning a token means this
  test passes. Never hand-roll an alert box: build on
  `KinetixAlert`, or on `statusAlertClass` with the `primitives/Alert*` parts.
- `role="auto"`: `danger` → `alert`, other colors → `status`. `region` uses the
  title as its accessible name. A translated, visually-hidden prefix
  ("Warning:") comes before the title (`sr-label` overrides it, `false` drops
  it). The title is a `<p>` unless `heading-level` is set.
- Closing: `dismiss-key` is required beyond `hide`. Closes are stored per user
  (`kinetix.dismissed:{userId}:{key}`). `device` syncs across tabs.
  `dismiss-duration` (ms) brings a `session`/`device` close back. A rejected
  `persist` re-opens the alert and emits `dismiss-error`. `v-model:open` back
  to `true` clears a stored close.
- `permanent` goes to the **Dismissals module** when
  `kinetix.dismissals.enabled` is on (migration tag
  `kinetix-dismissals-migrations`, table `kinetix_dismissals`): `POST
  {prefix}/dismissals {key, minutes?}` and `DELETE {prefix}/dismissals/{key}`
  (behind `auth`), plus a `kinetix_dismissals` prop with the user's active
  keys (null = module off). That prop is how an alert renders closed from the
  first frame on every device. A `persist` callback overrides the module, and
  with neither one, `permanent` falls back to `device`. Server side:
  `KinetixDismissals::has/dismiss/restore/keysFor($user, $key)`.
- On close, focus moves to `focusableNear()` and an announcement says whether
  the close was a dismissal or a hide-for-now.
- The first render doesn't animate unless `appear` is set.

## Flashed from the server

`<KinetixFlashAlerts />` (mounted once, where page messages belong) renders the
alerts a controller flashes:

```php
use Happones\Kinetix\Flash\KinetixFlash;

KinetixFlash::alert(__('billing.payment_failed'), 'danger')   // sends itself at statement end
    ->description(__('billing.payment_failed_body'));          // ->variant() ->icon() ->dismissible(false) ->id()

KinetixFlash::alert(__('auth.verify_email'), 'warning')->id('verify-email')->untilDismissed();
KinetixFlash::alert(__('onboarding.welcome'), 'success')->keep(2);   // next page + 2 more visits
KinetixFlash::forget('verify-email');                          // withdraw (not "closed by user")
```

- Default = one-shot over Inertia's `flash.kinetix.alerts`, never in history.
  The outlet collects them from the `flash` event, not from `page.flash`,
  because a poll or partial reload replaces that with `{}`. They stay while
  the path is the same and are cleared on `navigate` to another path.
- `keep(n)` / `untilDismissed()` → session (`kinetix_flash_alerts`) → shared
  prop `kinetix_alerts`, each with a server-made `dismissUrl`
  (`POST {prefix}/flash/{id}/dismiss`, `web` only, no team segment, guest-safe).
  A prefetch doesn't count as a showing. A closed stable `id` is remembered
  for the session, so re-sending it every request is safe.
- Client one-shot: `useKinetixFlash().alert(title, { color, description })`.
  `onKinetixFlash(handler)` gives the current page's flash and then every new
  one (it returns the unsubscribe).
- One-shot arrivals are announced (except `danger`, already `role="alert"`).
- Kinetix supports inertia-laravel `^2.0|^3.0`. Before 2.0.16 there is no
  `Inertia::flash()`, so the payload goes to session `kinetix_flash` and is
  shared as the `kinetix_flash` prop (`KinetixFlash::legacyPayload()`, null on
  newer versions). `onKinetixFlash` delivers it once per id. Never reference
  `Inertia\Support\SessionKey` (v3-only) without `class_exists`. Assert flashes in
  tests with `KinetixFlash::flashed()`, which works on both channels.

## Composables

- `useKinetixDismissal(key, { mode, duration, persist })` →
  `{ dismissed, dismiss(mode?), restore() }`. `persist` must be a plain
  function, never a getter (`toValue()` would call it).
- `useKinetixDismissalStore()` → `{ isDismissed, remember(key, scope, duration?), forget }`
  for many keys at once (the announcement banner keys each entry as
  `announcement:{id}`).
- `useKinetixTransition(preset)` → `<Transition v-bind>` props, `{ css: false }`
  under reduced motion. `collapse` needs a `grid` element with a `min-h-0`
  child.
- `useKinetixReducedMotion()`: OS setting, user preference, or
  `kinetix.motion = 'reduced'`.

i18n `alert_*` (7 locales). Tests: `KinetixAlert.spec.ts`,
`KinetixFlashAlerts.spec.ts`, `useKinetixFlash.spec.ts`,
`useKinetixDismissal.spec.ts`, `useKinetixTransition.spec.ts`,
`useKinetixReducedMotion.spec.ts`, `KinetixFlashTest`, `DismissalsTest`,
`MotionShareTest`.

Kinetix Alerts Development

When to Apply

Activate this skill when:

  • Showing an inline notice, callout, warning or error box inside a page.
  • Making a notice closable for this page, this tab, this browser, or for good.
  • Animating something that appears/disappears (reuse the transition presets).

For transient feedback after an action use a toast (KinetixFlash::success() or Notification::make()), not an alert. For product news use announcements.

Documentation

For full details, reference docs/alerts.md (published at https://happones.github.io/kinetix/alerts).

The component

<KinetixAlert
    color="warning"
    variant="soft"
    :title="t('billing.trial_ending_title')"
    :description="t('billing.trial_ending_body')"
    dismissible
    dismiss-mode="session"
    dismiss-key="trial-ending"
    dont-show-again
    :persist="saveDismissal"
    transition="collapse"
>
    <template #actions>…</template>
</KinetixAlert>

color: success · danger · warning · info · primary · gray. variant: soft · outline · accent. dismiss-mode: hide · session · device · permanent (persist = (key) => Promise stores a permanent close). transition: fade · slide-down · slide-up · scale · collapse · none.

  • soft / outline / accent: the color goes on the surface and the icon only; title and body stay on the foreground tokens. solid: the fill sets its own text color (-foreground; danger = text-white + dark:bg-destructive/60), and the icon, text and close button inherit it (text-current, button variant ghost-current). Surface classes come from statusAlertClass(color, variant) in useKinetixStatusColor. The banner takes no solid (its carousel controls are drawn for light surfaces).
  • Contrast is guarded by tests/js/statusTokenContrast.spec.ts, which parses kinetix.css and checks text-on-page, text-on-tint, solid fill and the white-on-red recipe at 4.5:1 in BOTH themes. Re-tuning a token means this test passes. Never hand-roll an alert box: build on KinetixAlert, or on statusAlertClass with the primitives/Alert* parts.
  • role="auto": danger → alert, other colors → status. region uses the title as its accessible name. A translated, visually-hidden prefix ("Warning:") comes before the title (sr-label overrides it, false drops it). The title is a <p> unless heading-level is set.
  • Closing: dismiss-key is required beyond hide. Closes are stored per user (kinetix.dismissed:{userId}:{key}). device syncs across tabs. dismiss-duration (ms) brings a session/device close back. A rejected persist re-opens the alert and emits dismiss-error. v-model:open back to true clears a stored close.
  • permanent goes to the Dismissals module when kinetix.dismissals.enabled is on (migration tag kinetix-dismissals-migrations, table kinetix_dismissals): POST {prefix}/dismissals {key, minutes?} and DELETE {prefix}/dismissals/{key} (behind auth), plus a kinetix_dismissals prop with the user's active keys (null = module off). That prop is how an alert renders closed from the first frame on every device. A persist callback overrides the module, and with neither one, permanent falls back to device. Server side: KinetixDismissals::has/dismiss/restore/keysFor($user, $key).
  • On close, focus moves to focusableNear() and an announcement says whether the close was a dismissal or a hide-for-now.
  • The first render doesn't animate unless appear is set.

Flashed from the server

<KinetixFlashAlerts /> (mounted once, where page messages belong) renders the alerts a controller flashes:

use Happones\Kinetix\Flash\KinetixFlash;

KinetixFlash::alert(__('billing.payment_failed'), 'danger')   // sends itself at statement end
    ->description(__('billing.payment_failed_body'));          // ->variant() ->icon() ->dismissible(false) ->id()

KinetixFlash::alert(__('auth.verify_email'), 'warning')->id('verify-email')->untilDismissed();
KinetixFlash::alert(__('onboarding.welcome'), 'success')->keep(2);   // next page + 2 more visits
KinetixFlash::forget('verify-email');                          // withdraw (not "closed by user")
  • Default = one-shot over Inertia's flash.kinetix.alerts, never in history. The outlet collects them from the flash event, not from page.flash, because a poll or partial reload replaces that with {}. They stay while the path is the same and are cleared on navigate to another path.
  • keep(n) / untilDismissed() → session (kinetix_flash_alerts) → shared prop kinetix_alerts, each with a server-made dismissUrl (POST {prefix}/flash/{id}/dismiss, web only, no team segment, guest-safe). A prefetch doesn't count as a showing. A closed stable id is remembered for the session, so re-sending it every request is safe.
  • Client one-shot: useKinetixFlash().alert(title, { color, description }). onKinetixFlash(handler) gives the current page's flash and then every new one (it returns the unsubscribe).
  • One-shot arrivals are announced (except danger, already role="alert").
  • Kinetix supports inertia-laravel ^2.0|^3.0. Before 2.0.16 there is no Inertia::flash(), so the payload goes to session kinetix_flash and is shared as the kinetix_flash prop (KinetixFlash::legacyPayload(), null on newer versions). onKinetixFlash delivers it once per id. Never reference Inertia\Support\SessionKey (v3-only) without class_exists. Assert flashes in tests with KinetixFlash::flashed(), which works on both channels.

Composables

  • useKinetixDismissal(key, { mode, duration, persist }) → { dismissed, dismiss(mode?), restore() }. persist must be a plain function, never a getter (toValue() would call it).
  • useKinetixDismissalStore() → { isDismissed, remember(key, scope, duration?), forget } for many keys at once (the announcement banner keys each entry as announcement:{id}).
  • useKinetixTransition(preset) → <Transition v-bind> props, { css: false } under reduced motion. collapse needs a grid element with a min-h-0 child.
  • useKinetixReducedMotion(): OS setting, user preference, or kinetix.motion = 'reduced'.

i18n alert_* (7 locales). Tests: KinetixAlert.spec.ts, KinetixFlashAlerts.spec.ts, useKinetixFlash.spec.ts, useKinetixDismissal.spec.ts, useKinetixTransition.spec.ts, useKinetixReducedMotion.spec.ts, KinetixFlashTest, DismissalsTest, MotionShareTest.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect