Skip to Content
DocumentationShame Props

Shame Props

Shame props are deliberately ugly-named boolean props that let you skip a required accessibility or safety check. They exist so the skip is visible in code review — no one can silently bypass a requirement.

Every shame prop triggers a console.warn in development mode with a link to the relevant WCAG guideline.


Why they exist

Sometimes you genuinely need to skip a check. A decorative image does not need alt text. A confirmation dialog might not need a visible title. These are valid cases.

But Kevlar refuses to let you skip silently. If you skip, the prop name screams it:

{/* This will show up in every PR diff */} <Image src="/decorative-swoosh.svg" badly_skip_alt_text_and_hurt_accessibility />

Reviewers see it. Future developers see it. The decision is documented in code, not buried in a comment.


All 9 shame props

Shame propWhat it skipsComponent(s)
badly_skip_alt_text_and_hurt_accessibilityImage/Avatar alt text requirementImage, Avatar
badly_allow_layout_shift_and_dont_define_sizeImage width+height or aspectRatio requirement (prevents CLS)Image
badly_skip_modal_title_and_hurt_accessibilityModal/Drawer title for screen readersModal, Drawer
badly_skip_aria_label_and_hurt_accessibilityActionIcon aria-label (icon-only buttons are invisible to screen readers)ActionIcon
badly_skip_input_label_and_hurt_accessibilityInput label for screen readersAll input components
dangerously_skip_offline_decisionNetwork onOffline slot (what happens when the user is offline)All interactive/input components
dangerously_skip_timeout_decisionTiming timeoutMs and onTimeout slots (what happens when an async action takes too long)All interactive components
dangerously_skip_focus_trapModal/Drawer focus trap (Tab.trap and trapFocus)Modal, Drawer
dangerously_allow_submit_type_buttonButton type="submit" override (Kevlar defaults to type="button" to prevent accidental form submissions)Button

Naming convention

  • badly_* — Accessibility violation. You are making the experience worse for users with disabilities.
  • dangerously_* — Safety/UX violation. You are removing a guard that prevents broken experiences.

Both prefixes use snake_case to make them visually distinct from normal camelCase props.


Dev mode warnings

In development, using any shame prop logs a warning to the console:

Kevlar: ActionIcon is using `badly_skip_aria_label_and_hurt_accessibility`. This skips the aria-label requirement for icon-only buttons. Screen reader users will not know what this button does. See: https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html

These warnings are stripped in production builds.


Usage examples

Decorative image (no alt text needed)

<Image src="/hero-pattern.svg" alt="" badly_skip_alt_text_and_hurt_accessibility width={1200} height={400} />

Even for decorative images, prefer passing alt="" (empty string) rather than omitting it entirely. The shame prop suppresses the error, but empty alt is the correct semantic choice.

ActionIcon with visible adjacent label

<Group> <ActionIcon onKevlarAction={handleDelete} announce={{ loading: 'Deleting...', success: 'Deleted', error: 'Delete failed' }} badly_skip_aria_label_and_hurt_accessibility > <TrashIcon /> </ActionIcon> <Text>Delete item</Text> </Group>
<Modal opened={opened} onClose={close} announce={{ open: 'Confirmation dialog opened' }} badly_skip_modal_title_and_hurt_accessibility aria-label="Confirm deletion" > <Text>Are you sure you want to delete this?</Text> </Modal>

Even when skipping the visible title, provide aria-label so screen readers still have context.

Skipping focus trap in a non-modal dialog

<Modal opened={opened} onClose={close} title="Quick preview" announce={{ open: 'Preview opened' }} dangerously_skip_focus_trap > {/* User can still interact with content behind this overlay */} </Modal>
Last updated on