CoolFace
Apppublic

lerobot/robot-learning-tutorial

sourceHugging Faceupdated 1y agoView on Hugging Face
508likes
Accordion.astro197 linesDownload Raw Back to components
1---2const { title, open = false, class: className, ...props } = Astro.props;3const wrapperClass = ["accordion", className].filter(Boolean).join(" ");4---5<details class={wrapperClass} open={open} {...props}>6  <summary class="accordion__summary">7    <span class="accordion__title">{title}</span>8    <svg class="accordion__chevron" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">9      <path d="M6 9l6 6 6-6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />10    </svg>11  </summary>12  <div class="accordion__content-wrapper">13    <div class="accordion__content">14      <slot />15    </div>16  </div>17</details>18 19<script>20  // Animate open/close by transitioning explicit height21  document.addEventListener('DOMContentLoaded', () => {22    const accordions = document.querySelectorAll('.accordion') as NodeListOf<HTMLDetailsElement>;23    accordions.forEach((acc) => {24      const summary = acc.querySelector('summary.accordion__summary') as HTMLElement | null;25      const wrapper = acc.querySelector('.accordion__content-wrapper') as HTMLElement | null;26      const content = acc.querySelector('.accordion__content') as HTMLElement | null;27      if (!summary || !wrapper || !content) return;28 29      const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;30      const duration = reduceMotion ? 0 : 240;31 32      // Initial state33      wrapper.style.overflow = 'hidden';34      wrapper.style.height = acc.open ? 'auto' : '0px';35 36      const open = () => {37        wrapper.style.height = '0px';38        void wrapper.offsetHeight; // reflow39        const target = content.scrollHeight;40        wrapper.style.transition = `height ${duration}ms ease`;41        wrapper.style.height = `${target}px`;42        const onEnd = () => {43          wrapper.style.transition = '';44          wrapper.style.height = 'auto';45          wrapper.removeEventListener('transitionend', onEnd);46        };47        wrapper.addEventListener('transitionend', onEnd);48      };49 50      const close = () => {51        const start = wrapper.offsetHeight || content.scrollHeight;52        wrapper.style.height = `${start}px`;53        void wrapper.offsetHeight; // reflow54        wrapper.style.transition = `height ${duration}ms ease`;55        wrapper.style.height = '0px';56        const onEnd = () => {57          wrapper.style.transition = '';58          wrapper.removeEventListener('transitionend', onEnd);59          acc.removeAttribute('open');60        };61        wrapper.addEventListener('transitionend', onEnd);62      };63 64      summary.addEventListener('click', (e) => {65        e.preventDefault();66        if (acc.open) {67          close();68        } else {69          acc.setAttribute('open', '');70          open();71        }72      });73    });74  });75  </script>76 77<style>78  .accordion {79    margin: 0 0 var(--spacing-4);80    padding: 0;81    border: 1px solid var(--border-color);82    border-radius: var(--table-border-radius);83    background: var(--surface-bg);84    transition: box-shadow 180ms ease, border-color 180ms ease;85  }86 87  .accordion[open] {88    border-color: color-mix(in oklab, var(--border-color), var(--primary-color) 20%);89  }90 91  .accordion[open] .accordion__summary {92    border-bottom: 1px solid var(--border-color);93  }94 95  .accordion__summary {96    margin: 0;97    list-style: none;98    display: flex;99    align-items: center;100    justify-content: space-between;101    gap: 4px;102    padding: var(--spacing-2) var(--spacing-3);103    cursor: pointer;104    color: var(--text-color);105    user-select: none;106  }107 108  /* Remove conditional padding to avoid jump on close */109 110  /* Remove native marker */111  .accordion__summary::-webkit-details-marker {112    display: none;113  }114  .accordion__summary::marker {115    content: "";116  }117 118  .accordion[size="big"] .accordion__summary {119    padding: 16px;120  }121 122  .accordion__title {123    font-weight: 600;124  }125 126  .accordion__chevron {127    flex: 0 0 auto;128    transition: transform 220ms ease;129    opacity: .85;130  }131 132  .accordion[open] .accordion__chevron {133    transform: rotate(180deg);134  }135 136  /* Animated expand/collapse using height (controlled in JS) */137  .accordion__content-wrapper {138    overflow: hidden;139    height: 0px;140    will-change: height;141    position: relative;142  }143 144  /* Subtle top shadow overlay when open (visible above children) */145  .accordion[open] .accordion__content-wrapper::before {146    content: "";147    position: absolute;148    left: 0;149    right: 0;150    top: 0;151    height: 6px;152    background: linear-gradient(to bottom, rgba(0,0,0,0.025), rgba(0,0,0,0));153    pointer-events: none;154    z-index: 1;155  }156 157  .accordion__content {158    margin: 0;159    padding: 0;160  }161 162  /* Ensure the very last slotted element has no bottom margin */163  .accordion .accordion__content > :global(*:last-child) {164    margin-bottom: 0 !important;165  }166  /* Remove bottom padding on last child, except for code-output blocks */167  .accordion .accordion__content > :global(:not(.code-output):last-child) {168    padding-bottom: 0 !important;169  }170 171  /* Ensure the very first slotted element has no top spacing */172  .accordion .accordion__content > :global(*:first-child) {173    margin-top: 0 !important;174  }175 176  /* Content padding: default for direct children, opt-out for code/tables */177  .accordion .accordion__content > :global(*) {178    padding: 8px;179  }180  .accordion .accordion__content > :global(.table-scroll),181  .accordion .accordion__content > :global(pre),182  .accordion .accordion__content > :global(.code-card) {183    padding: 0;184  }185 186  /* Focus styles for accessibility */187  .accordion__summary:focus-visible {188    outline: 2px solid var(--primary-color);189    outline-offset: 3px;190    border-radius: var(--table-border-radius);191  }192 193 194</style>195 196 197