Join

🚧 WORK IN PROGRESS: A CSS utility component that allows you to join two or more elements together. This looks best when the items have borders or background colors. 🚧

Class name Type
.prs-join Component Container
.prs-join-vertical Modifier Stacked
.prs-join-item Part Child container
  • Default

    The markup is very lenient in cases where deep nesting is required.

    <div class="prs-join">
      <button class="prs-btn prs-btn-secondary prs-join-item">1</button>
      <div><button class="prs-btn prs-btn-secondary prs-join-item">2</button></div>
      <button class="prs-btn prs-btn-secondary prs-join-item">3</button>
    </div>
    
  • Vertical

    The markup is very lenient in cases where deep nesting is required.

    <div class="prs-join prs-join-vertical">
      <button class="prs-btn prs-btn-secondary prs-join-item">1</button>
      <span><button class="prs-btn prs-btn-secondary prs-join-item">2</button></span>
      <button class="prs-btn prs-btn-secondary prs-join-item">3</button>
    </div>
    
  • Advanced

    An example of a real-world use case.

    <div class="prs-join w-full max-w-xs">
      <div>
        <label><input class="prs-input prs-join-item" placeholder="Search" aria-label="Search query" /></label>
      </div>
      <label>
        <select class="prs-input prs-join-item w-auto max-w-[6.25rem]" aria-label="Filter option">
          <option disabled selected>Filter</option>
          <option>Title</option>
          <option>Genre</option>
          <option>Option with a very long label...</option>
        </select>
      </label>
      <button class="prs-btn prs-btn-primary prs-btn-square prs-join-item" aria-label="Search">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" role="img">
          <use href="/_assets/prs-icons.svg#nav-search" />
        </svg>
      </button>
    </div>
    

CSS

Full Library

Component Only

.prs-join {
  --join-ss: 0;
  --join-se: 0;
  --join-es: 0;
  --join-ee: 0;
  display: inline-flex;
  align-items: stretch;
  & :where(.prs-join-item) {
    border-start-start-radius: var(--join-ss, 0);
    border-start-end-radius: var(--join-se, 0);
    border-end-end-radius: var(--join-ee, 0);
    border-end-start-radius: var(--join-es, 0);
    & * {
      --join-ss: var(--prs-radius-btn);
      --join-se: var(--prs-radius-btn);
      --join-es: var(--prs-radius-btn);
      --join-ee: var(--prs-radius-btn);
    }
  }
  & > .prs-join-item:where(:first-child) {
    --join-ss: var(--prs-radius-btn);
    --join-se: 0;
    --join-es: var(--prs-radius-btn);
    --join-ee: 0;
  }
  & :first-child:not(:last-child) {
    & :where(.prs-join-item) {
      --join-ss: var(--prs-radius-btn);
      --join-se: 0;
      --join-es: var(--prs-radius-btn);
      --join-ee: 0;
    }
  }
  & > .prs-join-item:where(:last-child) {
    --join-ss: 0;
    --join-se: var(--prs-radius-btn);
    --join-es: 0;
    --join-ee: var(--prs-radius-btn);
  }
  & :last-child:not(:first-child) {
    & :where(.prs-join-item) {
      --join-ss: 0;
      --join-se: var(--prs-radius-btn);
      --join-es: 0;
      --join-ee: var(--prs-radius-btn);
    }
  }
  & > .prs-join-item:where(:only-child) {
    --join-ss: var(--prs-radius-btn);
    --join-se: var(--prs-radius-btn);
    --join-es: var(--prs-radius-btn);
    --join-ee: var(--prs-radius-btn);
  }
  & :only-child {
    & :where(.prs-join-item) {
      --join-ss: var(--prs-radius-btn);
      --join-se: var(--prs-radius-btn);
      --join-es: var(--prs-radius-btn);
      --join-ee: var(--prs-radius-btn);
    }
  }
  & > :where(:not(:first-child)) .prs-join-item {
    margin-block-start: 0;
    margin-inline-start: calc(var(--prs-border-btn, 1px) * -1);
  }
}
.prs-join-item {
  &:where(:not(:first-child)) {
    margin-block-start: 0;
    margin-inline-start: calc(var(--prs-border-btn, 1px) * -1);
  }
  &:where(:focus-visible) {
    position: relative;
    z-index: 1;
  }
}
.prs-join-vertical {
  flex-direction: column;
  & > .prs-join-item:first-child {
    --join-ss: var(--prs-radius-btn);
    --join-se: var(--prs-radius-btn);
    --join-es: 0;
    --join-ee: 0;
  }
  & :first-child:not(:last-child) {
    & .prs-join-item {
      --join-ss: var(--prs-radius-btn);
      --join-se: var(--prs-radius-btn);
      --join-es: 0;
      --join-ee: 0;
    }
  }
  & > .prs-join-item:last-child {
    --join-ss: 0;
    --join-se: 0;
    --join-es: var(--prs-radius-btn);
    --join-ee: var(--prs-radius-btn);
  }
  & :last-child:not(:first-child) {
    & .prs-join-item {
      --join-ss: 0;
      --join-se: 0;
      --join-es: var(--prs-radius-btn);
      --join-ee: var(--prs-radius-btn);
    }
  }
  & > .prs-join-item:only-child {
    --join-ss: var(--prs-radius-btn);
    --join-se: var(--prs-radius-btn);
    --join-es: var(--prs-radius-btn);
    --join-ee: var(--prs-radius-btn);
  }
  & :only-child {
    & .prs-join-item {
      --join-ss: var(--prs-radius-btn);
      --join-se: var(--prs-radius-btn);
      --join-es: var(--prs-radius-btn);
      --join-ee: var(--prs-radius-btn);
    }
  }
  & > :where(:not(:first-child)) .prs-join-item {
    margin-block-start: calc(var(--prs-border-btn, 1px) * -1);
    margin-inline-start: 0;
  }
  & .prs-join-item {
    &:where(:not(:first-child)) {
      margin-block-start: calc(var(--prs-border-btn, 1px) * -1);
      margin-inline-start: 0;
    }
  }
}

Coming soon...