Disclosure
An interactive component which expands/collapses content.
Jasmin starred 3 repositories
import { CaretUpDown, X } from '@examples/primitives/disclosure/icons'
import Disclosure from '@corvu/disclosure'
import type { VoidComponent } from 'solid-js'
const DisclosureExample: VoidComponent = () => {
return (
<div class="mt-8">
<Disclosure collapseBehavior="hide">
{(props) => (
<>
<div class="mb-2 flex items-center justify-between space-x-4">
<p class="font-medium text-corvu-text-dark">
Jasmin starred 3 repositories
</p>
<Disclosure.Trigger class="rounded-lg bg-corvu-100 p-1 transition-all duration-100 hover:bg-corvu-200 active:translate-y-0.5">
{props.expanded && (
<>
<X size="20" />
<span class="sr-only">Collapse</span>
</>
)}
{!props.expanded && (
<>
<CaretUpDown size="20" />
<span class="sr-only">Expand</span>
</>
)}
</Disclosure.Trigger>
</div>
<div class="rounded-lg bg-corvu-100 px-3 py-2">corvudev/corvu</div>
<Disclosure.Content class="mt-1 space-y-1 overflow-hidden data-expanded:animate-expand data-collapsed:animate-collapse">
<div class="rounded-lg bg-corvu-100 px-3 py-2">solidjs/solid</div>
<div class="rounded-lg bg-corvu-100 px-3 py-2">
nitropage/nitropage
</div>
</Disclosure.Content>
</>
)}
</Disclosure>
</div>
)
}
export default DisclosureExample
@import 'tailwindcss';
@theme {
--color-corvu-text: #180f24;
--color-corvu-bg: #f3f1fe;
--color-corvu-100: #e6e2fd;
--color-corvu-200: #d4cbfb;
--color-corvu-300: #bcacf6;
--color-corvu-400: #a888f1;
--animate-expand: expand 250ms cubic-bezier(0.32,0.72,0,0.75);
--animate-collapse: collapse 250ms cubic-bezier(0.32,0.72,0,0.75);
--animate-caret-blink: caret-blink 1.25s ease-out infinite;
--animate-in: enter 150ms ease;
--animate-out: exit 150ms ease;
@keyframes expand {
0% {
height: 0px;
}
100% {
height: var(--corvu-disclosure-content-height);
}
}
@keyframes collapse {
0% {
height: var(--corvu-disclosure-content-height);
}
100% {
height: 0px;
}
}
@keyframes caret-blink {
0%, 70%, 100% {
opacity: 0;
}
20%, 50% {
opacity: 1;
}
}
@keyframes enter {
from {
opacity: var(--tw-enter-opacity, 1);
transform: translate3d(
var(--tw-enter-translate-x, 0),
var(--tw-enter-translate-y, 0),
0
)
scale3d(
var(--tw-enter-scale, 1),
var(--tw-enter-scale, 1),
var(--tw-enter-scale, 1)
)
}
}
@keyframes exit {
to {
opacity: var(--tw-exit-opacity, 1);
transform: translate3d(
var(--tw-exit-translate-x, 0),
var(--tw-exit-translate-y, 0),
0
)
scale3d(
var(--tw-exit-scale, 1),
var(--tw-exit-scale, 1),
var(--tw-exit-scale, 1)
)
}
}
}
@utility fade-in-* {
--tw-enter-opacity: --value(percentage, ratio);
}
@utility fade-out-* {
--tw-exit-opacity: --value(percentage, ratio);
}
@utility zoom-in-* {
--tw-enter-scale: --value(percentage, ratio);
}
@utility zoom-out-* {
--tw-exit-scale: --value(percentage, ratio);
}
@utility slide-in-from-top-* {
--tw-enter-translate-y: calc(--value(percentage) * -1);
--tw-enter-translate-y: calc(--value(ratio) * -100%);
--tw-enter-translate-y: calc(--value(integer) * var(--spacing) * -1);
}
@utility slide-in-from-bottom-* {
--tw-enter-translate-y: --value(percentage);
--tw-enter-translate-y: calc(--value(ratio) * 100%);
--tw-enter-translate-y: calc(--value(integer) * var(--spacing));
}
@utility slide-out-to-top-* {
--tw-exit-translate-y: calc(--value(percentage) * -1);
--tw-exit-translate-y: calc(--value(ratio) * -100%);
--tw-exit-translate-y: calc(--value(integer) * var(--spacing) * -1);
}
@utility slide-out-to-bottom-* {
--tw-exit-translate-y: --value(percentage);
--tw-exit-translate-y: calc(--value(ratio) * 100%);
--tw-exit-translate-y: calc(--value(integer) * var(--spacing));
}
@plugin '@tailwindcss/forms';
import './index.css'
import { CaretUpDown, X } from '@examples/primitives/disclosure/icons'
import Disclosure from '@corvu/disclosure'
import type { VoidComponent } from 'solid-js'
const DisclosureExample: VoidComponent = () => {
return (
<div>
<Disclosure collapseBehavior="hide">
{(props) => (
<>
<div class="header">
<p class="header_title">Jasmin starred 3 repositories</p>
<Disclosure.Trigger>
{props.expanded && (
<>
<X size="20" />
<span class="sr-only">Collapse</span>
</>
)}
{!props.expanded && (
<>
<CaretUpDown size="20" />
<span class="sr-only">Expand</span>
</>
)}
</Disclosure.Trigger>
</div>
<div class="repository_card">corvudev/corvu</div>
<Disclosure.Content>
<div class="repository_card">solidjs/solid</div>
<div class="repository_card">nitropage/nitropage</div>
</Disclosure.Content>
</>
)}
</Disclosure>
</div>
)
}
export default DisclosureExample
.header {
margin-bottom: 0.5rem;
display: flex;
align-items: center;
justify-content: space-between;
}
.header> :not([hidden])~ :not([hidden]) {
margin-left: 1rem;
}
.header_title {
font-weight: 500;
}
[data-corvu-disclosure-trigger] {
border-radius: 0.5rem;
background-color: hsl(249, 87%, 94%);
padding: 0.25rem;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 100ms;
}
[data-corvu-disclosure-trigger]:active {
transform: translate(0, 0.125rem);
}
[data-corvu-disclosure-trigger]:hover {
background-color: hsl(251, 86%, 89%);
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.repository_card {
border-radius: 0.5rem;
background-color: hsl(249, 87%, 94%);
padding-left: 0.75rem;
padding-right: 0.75rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
[data-corvu-disclosure-content] {
margin-top: 0.25rem;
overflow: hidden;
}
.space-y-1 > :not([hidden]) ~ :not([hidden]){
margin-top: 0.25rem;
}
[data-corvu-disclosure-content][data-collapsed] {
animation: collapse 200ms linear;
}
[data-corvu-disclosure-content][data-expanded] {
animation: expand 200ms linear;
}
@keyframes expand {
0% {
height: 0px;
}
100% {
height: var(--corvu-disclosure-content-height);
}
}
@keyframes collapse {
0% {
height: var(--corvu-disclosure-content-height);
}
100% {
height: 0px;
}
}
Features Section titled Features
- Option to hide the content when collapsed instead of unmounting it for better SEO
- CSS variables to animate the height/width of the content
- Full keyboard navigation
Installation Section titled Installation
npm install @corvu/disclosure
The disclosure is also included in the main corvu
package under corvu/disclosure
.
Usage Section titled Usage
import Disclosure from '@corvu/disclosure' // 'corvu/disclosure'
// or
// import { Root, Trigger, Content } from '@corvu/disclosure'
Anatomy Section titled Anatomy
<Disclosure>
<Disclosure.Trigger />
<Disclosure.Content />
</Disclosure>
Animation Section titled Animation
corvu sets the --corvu-disclosure-content-height
and --corvu-disclosure-content-width
CSS properties on the disclosure content that make it possible to animate the height/width.
[data-corvu-disclosure-content][data-collapsed] {
animation: collapse 200ms linear;
}
[data-corvu-disclosure-content][data-expanded] {
animation: expand 200ms linear;
}
@keyframes collapse {
0% {
height: var(--corvu-disclosure-content-height);
}
100% {
height: 0px;
}
}
@keyframes expand {
0% {
height: 0px;
}
100% {
height: var(--corvu-disclosure-content-height);
}
}
Accessibility Section titled Accessibility
Adheres to the Disclosure WAI-ARIA design pattern.
Keyboard navigation Section titled Keyboard navigation
Key | Behavior |
---|---|
Space | Activates the trigger and toggles the disclosure. |
Enter | Activates the trigger and toggles the disclosure. |
API reference Section titled API reference
Context wrapper for the disclosure. Is required for every disclosure you create.
Props
Property | Default | Type/Description |
---|---|---|
expanded | - | boolean Whether the disclosure is expanded. |
onExpandedChange | - | (expanded: boolean) => void Callback fired when the expanded state changes. |
initialExpanded | false | boolean Whether the disclosure is expanded initially. |
collapseBehavior | remove | 'remove' | 'hide' Whether the disclosure content should be removed or hidden when collapsed. Useful if you want to always render the content for SEO reasons. |
onContentPresentChange | - | (present: boolean) => void Callback fired when the disclosure content present state changes. |
disclosureId | createUniqueId() | string The id attribute of the disclosure content element. |
contextId | - | string The id of the disclosure context. Useful if you have nested disclosures and want to create components that belong to a disclosure higher up in the tree. |
Button that changes the open state of the disclosure when clicked.
Props
Property | Default | Type/Description |
---|---|---|
as | button | ValidComponent Component to render the dynamic component as. |
contextId | - | string The id of the disclosure context to use. |
Data attributes
Data attributes present on <Trigger />
components.
Property | Description |
---|---|
data-corvu-disclosure-trigger | Present on every disclosure trigger element. |
data-expanded | Present when the disclosure is expanded. |
data-collapsed | Present when the disclosure is collapsed. |
Content of a disclosure. Can be animated.
Props
Property | Default | Type/Description |
---|---|---|
as | div | ValidComponent Component to render the dynamic component as. |
forceMount | false | boolean Whether the disclosure content should be forced to render. Useful when using third-party animation libraries. |
contextId | - | string The id of the disclosure context to use. |
Data attributes
Data attributes present on <Content />
components.
Property | Description |
---|---|
data-corvu-disclosure-content | Present on every disclosure content element. |
data-expanded | Present when the disclosure is expanded. |
data-collapsed | Present when the disclosure is collapsed. |
CSS properties
CSS properties present on <Content />
components.
Property | Description |
---|---|
--corvu-disclosure-content-width | The width of the disclosure content. Useful if you want to animate its width. |
--corvu-disclosure-content-height | The height of the disclosure content. Useful if you want to animate its height. |
Context which exposes various properties to interact with the disclosure. Optionally provide a contextId to access a keyed context.
Returns
Property | Type/Description |
---|---|
expanded | Accessor<boolean> Whether the disclosure is expanded. |
setExpanded | Setter<boolean> Callback fired when the expanded state changes. |
collapseBehavior | Accessor<'remove' | 'hide'> Whether the disclosure content should be removed or hidden when collapsed. |
disclosureId | Accessor<string> The id attribute of the disclosure content element. |
contentPresent | Accessor<boolean> Whether the disclosure content is present. This differes from expanded as it tracks pending animations. |
contentRef | Accessor<HTMLElement | null> The disclosure content element. |
contentSize | Accessor<[number, number] | null> The current size of the disclosure content. Useful if you want to animate width or height. [width, height] |
Props that are passed to the Root component children callback.
Props
Property | Type/Description |
---|---|
expanded | boolean Whether the disclosure is expanded. |
setExpanded | Setter<boolean> Callback fired when the expanded state changes. |
collapseBehavior | 'remove' | 'hide' Whether the disclosure content should be removed or hidden when collapsed. |
disclosureId | string The id attribute of the disclosure content element. |
contentPresent | boolean Whether the disclosure content is present. This differes from expanded as it tracks pending animations. |
contentRef | HTMLElement | null The disclosure content element. |
contentSize | [number, number] | null The current size of the disclosure content. Useful if you want to animate width or height. [width, height] |
Developed and designed by Jasmin