Pressable Feedback
A press interaction layer adding ripple, highlight, hold-to-confirm, and progress feedback effects to any element
Import
import { PressableFeedback } from '@darkcode-ui/react';Anatomy
Import the PressableFeedback component and access all parts using dot notation. The root
renders a button by default and exposes hover / press / focus state through data attributes,
so the feedback layers can compose freely on top of any content.
<PressableFeedback>
<PressableFeedback.Highlight />
<PressableFeedback.Ripple />
<PressableFeedback.HoldConfirm />
<PressableFeedback.ProgressFeedback />
</PressableFeedback>Usage
import {PressableFeedback} from "@darkcode-ui/react";
export function PressableFeedbackDefault() {
return (
<PressableFeedback className="relative flex min-w-40 items-center justify-center gap-2 rounded-2xl border border-border bg-surface px-6 py-4 text-sm font-medium text-foreground">Comparison
import {PressableFeedback} from "@darkcode-ui/react";
const surface =
"relative flex min-w-36 items-center justify-center gap-2 rounded-2xl border border-border bg-surface px-6 py-4 text-sm font-medium text-foreground";
Disabled
import {PressableFeedback} from "@darkcode-ui/react";
const surface =
"relative flex min-w-36 items-center justify-center gap-2 rounded-2xl border border-border bg-surface px-6 py-4 text-sm font-medium text-foreground";
With Highlight
import {PressableFeedback} from "@darkcode-ui/react";
export function PressableFeedbackWithHighlight() {
return (
<PressableFeedback className="relative flex min-w-40 items-center justify-center gap-2 rounded-2xl border border-border bg-surface px-6 py-4 text-sm font-medium text-foreground">
<PressableFeedback.Highlight />
<span className="relative z-[1]">Highlight</span>
</PressableFeedback>
);
}Standalone Highlight
import {PressableFeedback} from "@darkcode-ui/react";
export function PressableFeedbackStandaloneHighlight() {
return (
<PressableFeedback className="relative flex size-16 items-center justify-center rounded-2xl bg-accent text-accent-foreground">
<PressableFeedback.Highlight />
<span className="relative z-[1] text-lg">★</span>
</PressableFeedback>
);
}With Ripple
import {PressableFeedback} from "@darkcode-ui/react";
export function PressableFeedbackWithRipple() {
return (
<PressableFeedback className="relative flex min-w-40 items-center justify-center gap-2 rounded-2xl border border-border bg-surface px-6 py-4 text-sm font-medium text-foreground">
<PressableFeedback.Ripple />
<span className="relative z-[1]">Ripple</span>
</PressableFeedback>
);
}Standalone Ripple
import {PressableFeedback} from "@darkcode-ui/react";
export function PressableFeedbackStandaloneRipple() {
return (
<PressableFeedback className="relative flex size-16 items-center justify-center rounded-full border border-border bg-surface text-foreground">
<PressableFeedback.Ripple />
<span className="relative z-[1] text-lg">+</span>
</PressableFeedback>
);
}With Hold Confirm
import {PressableFeedback} from "@darkcode-ui/react";
import {Icon} from "@iconify/react";
export function PressableFeedbackWithHoldConfirm() {
return (Hold Confirm Callback
"use client";
import {PressableFeedback} from "@darkcode-ui/react";
import {Icon} from "@iconify/react";
import {useState} from "react";Hold Confirm Durations
import {PressableFeedback} from "@darkcode-ui/react";
const surface =
"relative flex min-w-36 items-center justify-center gap-2 rounded-2xl border border-border bg-surface px-6 py-4 text-sm font-medium text-foreground";
Hold Confirm Sweep
import type {ComponentProps} from "react";
import {PressableFeedback} from "@darkcode-ui/react";
type Sweep = NonNullable<ComponentProps<typeof PressableFeedback.HoldConfirm>["sweep"]>;With Progress Feedback
import {PressableFeedback} from "@darkcode-ui/react";
import {Icon} from "@iconify/react";
export function PressableFeedbackWithProgressFeedback() {
return (Progress Feedback Callback
"use client";
import {PressableFeedback} from "@darkcode-ui/react";
import {Icon} from "@iconify/react";
import {useState} from "react";Progress Feedback Durations
import {PressableFeedback} from "@darkcode-ui/react";
const surface =
"relative flex min-w-36 items-center justify-center gap-2 rounded-2xl border border-border bg-surface px-6 py-4 text-sm font-medium text-foreground";
Progress Feedback No Reset
import {PressableFeedback} from "@darkcode-ui/react";
import {Icon} from "@iconify/react";
export function PressableFeedbackProgressFeedbackNoReset() {
return (Progress Feedback Sweep
import type {ComponentProps} from "react";
import {PressableFeedback} from "@darkcode-ui/react";
type Sweep = NonNullable<ComponentProps<typeof PressableFeedback.ProgressFeedback>["sweep"]>;Pressable Cards
import {PressableFeedback} from "@darkcode-ui/react";
const plans = [
{desc: "For individuals getting started", title: "Starter"},
{desc: "For growing teams that need more", title: "Pro"},Styling
Passing Tailwind CSS classes
The root and every feedback layer accept a className. Keep your own content above the
overlays with relative z-[1]:
import {PressableFeedback} from '@darkcode-ui/react';
function CustomPressable() {
return (
<PressableFeedback className="rounded-2xl border border-border bg-surface px-6 py-4">
<PressableFeedback.Highlight />
<PressableFeedback.Ripple />
<span className="relative z-[1]">Press me</span>
</PressableFeedback>
);
}Customizing the component classes
To customize the PressableFeedback classes, you can use the @layer components directive.
Learn more.
@layer components {
.pressable-feedback {
--pressable-feedback-highlight-color: var(--accent);
--pressable-feedback-highlight-opacity: 0.12;
}
.pressable-feedback__hold-confirm {
background-color: var(--danger);
color: var(--danger-foreground);
}
}DarkCode UI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.
CSS Classes
The PressableFeedback component uses these CSS classes (View source styles):
Base Classes
.pressable-feedback- Base pressable container with relative positioning and overflow hidden
Element Classes
.pressable-feedback__highlight- Opacity-based press overlay.pressable-feedback__ripple- M3-style radial ripple container.pressable-feedback__ripple-surface- Ripple animation surface with::before(hover) and::after(press) pseudo-elements.pressable-feedback__hold-confirm- Clip-path hold-to-reveal overlay.pressable-feedback__progress-feedback- Clip-path click-to-progress overlay
Interactive States
- Focus visible:
:focus-visibleor[data-focus-visible="true"]on.pressable-feedback(focus ring) - Disabled:
:disabledor[aria-disabled="true"]on.pressable-feedback(reduced opacity, no pointer events) - Hover:
:hoveror[data-hovered="true"]on the root activates.pressable-feedback__highlightopacity - Pressed:
:activeor[data-pressed="true"]on the root activates the pressed opacity
Data Attributes
[data-sweep="right|left|down|up"]on hold-confirm and progress-feedback - Clip-path sweep direction[data-holding="true"]on.pressable-feedback__hold-confirm- Currently being held[data-complete="true"]on hold-confirm/progress-feedback - Action completed[data-progressing="true"]on.pressable-feedback__progress-feedback- Progress is active
CSS Variables
--pressable-feedback-highlight-color- Highlight overlay color (default:currentColor)--pressable-feedback-highlight-opacity- Hover opacity (default:0.08)--pressable-feedback-highlight-pressed-opacity- Pressed opacity (default:0.12)--pressable-feedback-ripple-color- Ripple color (default:currentColor)--pressable-feedback-ripple-hover-opacity- Ripple hover opacity (default:0.08)--pressable-feedback-ripple-pressed-opacity- Ripple pressed opacity (default:0.12)--pressable-feedback-hold-confirm-duration- Hold duration (default:2000ms)--pressable-feedback-hold-confirm-release-duration- Release snap-back duration (default:200ms)--pressable-feedback-progress-feedback-duration- Progress duration (default:2000ms)--pressable-feedback-progress-feedback-release-duration- Reset snap-back duration (default:300ms)
API Reference
PressableFeedback
The root pressable container. Renders a button element by default.
| Prop | Type | Default | Description |
|---|---|---|---|
isDisabled | boolean | false | Whether the pressable is disabled |
children | ReactNode | - | Feedback layers and content |
className | string | - | Additional CSS class |
render | DOMRenderFunction | - | Custom render function to override the default button element |
Also supports all native button HTML attributes.
PressableFeedback.Highlight
Opacity-based hover/press overlay. No additional props beyond standard span attributes.
PressableFeedback.Ripple
M3-style radial ripple effect.
| Prop | Type | Default | Description |
|---|---|---|---|
duration | number | 150 | Duration in ms for the ripple grow animation |
hoverOpacity | number | 0.08 | Opacity of the hover state |
pressedOpacity | number | 0.12 | Opacity of the pressed state |
minimumPressDuration | number | 225 | Minimum press duration in ms |
isDisabled | boolean | - | Whether the ripple is disabled |
className | string | - | Additional CSS class |
style | CSSProperties | - | Additional inline styles |
PressableFeedback.HoldConfirm
Clip-path hold-to-reveal overlay.
| Prop | Type | Default | Description |
|---|---|---|---|
duration | number | 2000 | Hold duration in ms before the action is confirmed |
releaseDuration | number | 200 | Duration in ms for the snap-back animation on release |
sweep | 'right' | 'left' | 'down' | 'up' | 'right' | Which edge the clip-path reveal sweeps toward |
resetOnComplete | boolean | true | Whether to reset the overlay after the hold completes |
isDisabled | boolean | - | Whether the hold confirm is disabled |
onComplete | () => void | - | Fired when the hold reaches the full duration |
children | ReactNode | - | Overlay content shown during the reveal |
className | string | - | Additional CSS class |
PressableFeedback.ProgressFeedback
Clip-path click-to-progress overlay (auto, no hold required).
| Prop | Type | Default | Description |
|---|---|---|---|
duration | number | 2000 | Progress duration in ms before the action is confirmed |
releaseDuration | number | 300 | Duration in ms for the snap-back animation on reset |
sweep | 'right' | 'left' | 'down' | 'up' | 'right' | Which edge the clip-path reveal sweeps toward |
autoReset | boolean | true | Whether to automatically reset after completing |
resetDelay | number | 1500 | Delay in ms before resetting after completion |
isDisabled | boolean | - | Whether the progress feedback is disabled |
onComplete | () => void | - | Fired when the progress reaches the full duration |
onReset | () => void | - | Fired when the overlay resets back to idle |
children | ReactNode | - | Overlay content shown during the reveal |
className | string | - | Additional CSS class |