This component provides the commonly used Close Button with an icon.
It is also used under other components in Frontile, for example Modal and Drawer
import { CloseButton } from 'frontile';
import { CloseButton } from 'frontile';
<template><CloseButton /></template>
import { CloseButton } from 'frontile';
<template>
<div class='flex items-center space-x-2'>
<CloseButton @size='xs' />
<CloseButton @size='sm' />
<CloseButton @size='md' />
<CloseButton @size='lg' />
<CloseButton @size='xl' />
</div>
</template>
The CloseButton component supports press interactions through the @onPress callback, providing cross-platform support for mouse, touch, and keyboard events.
import { CloseButton } from 'frontile';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
export default class CloseButtonExample extends Component {
@tracked isVisible = true;
handleClose = () => {
this.isVisible = false;
// Reset after a delay for demo purposes
setTimeout(() => {
this.isVisible = true;
}, 2000);
};
<template>
<div class='flex items-center space-x-4'>
{{#if this.isVisible}}
<div
class='bg-primary-soft border border-primary rounded p-4 flex items-center justify-between'
>
<span>This is a dismissible message</span>
<CloseButton @onPress={{this.handleClose}} />
</div>
{{else}}
<p class='text-neutral'>Message dismissed (will reappear in 2 seconds)</p>
{{/if}}
</div>
</template>
}
CloseButtons automatically track their pressed state and add a data-pressed attribute when being pressed, which can be used for styling:
button[data-pressed='true'] {
transform: scale(0.95);
transition: transform 0.1s ease;
}
The icon is aria-hidden, and the accessible name comes from @title rendered
inside a
VisuallyHidden
— so the
button always has a name, defaulting to "Close", without any visible text.
| Key | Behaviour |
|---|---|
Tab |
Moves focus to the button. disabled removes it from the tab order. |
Enter |
Activates it, firing @onPress. |
Space |
Activates it, firing @onPress. |
Because the press modifier calls preventDefault() on Enter and Space, the
browser never synthesises a click — so use @onPress rather than a
{{on "click"}} handler, or keyboard users get nothing. This is the same reason
@onClick is deprecated.
@title when there is more than one"Close" is a fine name for the single dismiss button on a modal. It is a poor one in a list, where every button is announced identically and none of them says what it closes. Name the thing:
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { concat, fn } from '@ember/helper';
import { CloseButton } from 'frontile';
export default class Example extends Component {
@tracked messages = ['Deploy finished', 'Backup complete', 'Invite sent'];
dismiss = (message: string): void => {
this.messages = this.messages.filter((item) => item !== message);
};
<template>
<ul role='list' aria-label='Notifications' class='flex flex-col gap-2'>
{{#each this.messages as |message|}}
<li
class='bg-surface-card border border-neutral-soft rounded flex items-center justify-between gap-4 p-3'
>
<span>{{message}}</span>
<CloseButton
@title={{concat 'Dismiss: ' message}}
@onPress={{fn this.dismiss message}}
/>
</li>
{{else}}
<li class='text-neutral'>Nothing left to dismiss.</li>
{{/each}}
</ul>
</template>
}
Passing a block replaces the default icon, and with it the aria-hidden that
kept the icon out of the accessible name. Mark your own icon aria-hidden='true'
so the name stays whatever @title says:
import { CloseButton } from 'frontile';
import { DeleteIcon } from 'site/components/icons';
<template>
<CloseButton @title='Delete draft' as |iconClass|>
<DeleteIcon class={{iconClass}} aria-hidden='true' />
</CloseButton>
</template>
Removing something on press leaves focus on a button that no longer exists, which drops it back to the top of the document. Move focus to the next item or to the container that held the removed one.
If you're currently using @onClick, you should migrate to @onPress for better cross-platform support:
// ❌ Deprecated - will show a deprecation warning
<CloseButton @onClick={{this.handleClick}} />
// ✅ Recommended - better cross-platform support
<CloseButton @onPress={{this.handlePress}} />
The @onClick argument is still supported, but has been deprecated since v0.17
and will be removed in a future release. @onPress covers the same cases and
more:
Element: HTMLButtonElement
| Name | Type | Default | Description |
|---|---|---|---|
class
|
string
|
- | Additional class for close button element |
onClick
Deprecated
|
function
|
- |
Deprecated. Use onPress instead for better cross-platform support Deprecated: The function to call when button is clicked |
onPress
|
function
|
- | The function to call when button is pressed |
size
|
enum
|
'lg'
|
The icon size |
title
|
string
|
'Close'
|
The title of the close button |
variant
Deprecated
|
enum
|
'ghost'
`transparent` and `subtle` are
|
Deprecated. — use `ghost` and `soft` instead. |
| Name | Type | Default | Description |
|---|---|---|---|
default
*
|
Array
|
- |