This component no longer exists. It was in ns-ui’s manifest when we first indexed it and is not there now — the registry served its inventory on 2026-08-07 and this item was not on it. The install command below will fail. It is kept here because a tutorial somewhere still tells people to run it.
Banner Tear Stub
ns-ui/banner-tear-stubRemovedComponent
A dismissible notice with a perforated edge: dismissing rips the panel off along the perforation, leaving a small permanent stub that reopens it.
Live preview
live · rendered by the registry
Rendered by ns-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://design.helpmarq.com/r/banner-tear-stub.jsonSource
1"use client";23import { useCallback, useEffect, useId, useRef, useState, type ReactNode } from "react";45// ---------------------------------------------------------------------------6// TearStub — a dismissible notice with a perforated edge, 32px in from the7// leading edge. Dismissing tears the panel off along that perforation with a8// quick ~1deg rip (translate + rotate + fade, ease-out-expo) while a slim9// stub stays seated flush at the edge — a permanent, always-legible record10// that a notice exists, exactly like the stub a receipt book keeps after the11// check tears out. Clicking the stub reopens the banner, reversing the rip12// and springing the row's height back open. The stub itself is always13// present as decoration while attached (just the icon, non-interactive) and14// is promoted into the Reopen button only once the panel is actually gone —15// so there is never a pointless enabled control sitting idle at rest.16//17// Differs from truncation-word-count on purpose: that folds content away in place and18// keeps the same object at a smaller size. Here the remainder is a distinct,19// much smaller object (a torn stub, not a folded panel) and the physical20// contract is that the record is never destroyed, only shrunk — dismissal21// is always recoverable.22//23// This component doesn't force a width — it's an inline flex row sized to24// its content. Pass className="w-full" for a page-width banner; either way,25// once the panel is torn off, only the stub column remains in the row, so26// the whole thing naturally shrinks down to a small tab rather than leaving27// a wide empty strip.28//29// DOM + CSS only, no canvas. Colors come from --background/--surface/30// --foreground/--muted/--border/--accent only. prefers-reduced-motion swaps31// the rip for a plain crossfade (handled by the stylesheet below) and the32// height change for a short linear resize instead of a spring (handled in33// JS, since a spring is inherently a physical motion).34// ---------------------------------------------------------------------------3536const TEAR_MS = 300;37const TEAR_MS_REDUCED = 160;38const TEAR_EASE = "cubic-bezier(0.22, 1, 0.36, 1)"; // ease-out-expo-ish39const SPRING_STIFFNESS = 230; // s^-240const SPRING_DAMPING = 26; // ~critically damped, one barely-visible settle41const STUB_HIT_OVERHANG = 6; // each side of the 32px (w-8) column — 44px touch target4243function NoticeIcon({ urgent, className }: { urgent: boolean; className?: string }) {44 if (urgent) {45// … truncated
