refactor pricing table: moving to grid from flex boxes, border misalignments fix (#2214)

This commit is contained in:
trean
2026-03-17 22:09:37 +01:00
committed by GitHub
parent 21df341ef7
commit c8d77862d6
5 changed files with 199 additions and 128 deletions
@@ -28,6 +28,7 @@ $secondary-blue-30: #0040a1;
$secondary-blue-50: #2f6ff0;
$secondary-blue-70: #89a9ff;
$secondary-blue-90: #d9e2fe;
$secondary-blue-95: #f5f8ff;
$secondary-violet-10: #24005b;
$secondary-violet-30: #5700c9;
@@ -1,64 +1,63 @@
@use '../../helpers/functions' as *;
@use 'sass:math';
/* Shared pricing table styles – used by qdrant-pricing-features and qdrant-pricing-comparison */
:root {
--tier-count: 4;
--feature-cell-width: 19.375rem;
}
.pricing-table {
// Layout – fallback for browsers without subgrid (same column track sizes, explicit grid)
// First column: up to --feature-cell-width when grid fits, shrinks when it doesn't
&__table {
overflow: hidden;
display: grid;
grid-template-columns: minmax(0, var(--feature-cell-width)) repeat(var(--tier-count), 1fr);
}
&__table-header {
display: flex;
&__table-header,
&__table-footer {
grid-column: 1 / calc(var(--tier-count) + 2);
display: grid;
grid-template-columns: minmax(0, var(--feature-cell-width)) repeat(var(--tier-count), 1fr);
}
& .pricing-table__table-cell {
border-bottom: 0;
&:first-child {
border-right: 0;
}
&:nth-child(2) {
border-left: 0.5px solid $neutral-80;
border-top-left-radius: pxToRem(8);
}
&:not(:first-of-type) {
border-top: 0.5px solid $neutral-80;
}
&:last-child {
border-right: 0.5px solid $neutral-80;
border-top-right-radius: pxToRem(8);
}
}
&__table-body {
grid-column: 1 / calc(var(--tier-count) + 2);
display: block;
}
&__table-section {
&:first-of-type &-header {
border-top: 0.5px solid $neutral-80;
border-top-left-radius: pxToRem(8);
}
&:last-of-type.pricing-table__table-section--collapsed &-header {
border-bottom-left-radius: pxToRem(8);
}
display: grid;
grid-template-columns: minmax(0, var(--feature-cell-width)) repeat(var(--tier-count), 1fr);
&-header {
grid-column: 1 / calc(var(--tier-count) + 2);
display: flex;
align-items: center;
justify-content: space-between;
padding: $spacer * 1.5 $spacer * 2;
background-color: #f5f8ff;
border-bottom: 0.5px solid $neutral-80;
border-right: 0.5px solid $neutral-80;
border-left: 0.5px solid $neutral-80;
padding: pxToRem(24) pxToRem(32);
cursor: pointer;
user-select: none;
background-color: $secondary-blue-95;
border-left: 0.5px solid $neutral-80;
border-right: 0.5px solid $neutral-80;
border-bottom: 0.5px solid $neutral-80;
}
&-title {
font-size: pxToRem(16);
font-weight: 500;
line-height: 1.5;
color: $neutral-20;
}
&-rows {
display: grid;
grid-template-columns: minmax(0, var(--feature-cell-width)) repeat(var(--tier-count), 1fr);
grid-column: 1 / calc(var(--tier-count) + 2);
transition: max-height 0.35s ease;
}
@@ -67,111 +66,187 @@
transform: rotate(180deg);
}
}
}
&__chevron {
color: $neutral-20;
transition: transform 0.2s ease;
}
&:first-of-type &-header {
border-top: 0.5px solid $neutral-80;
border-top-left-radius: pxToRem(8);
}
&__table-row {
display: flex;
background-color: $neutral-98;
border-bottom: 0.5px solid $neutral-80;
border-right: 0.5px solid $neutral-80;
border-left: 0.5px solid $neutral-80;
}
&__table-body {
.pricing-table__table-section:last-of-type
.pricing-table__table-section-rows
.pricing-table__table-row:last-child {
&:last-of-type.pricing-table__table-section--collapsed &-header {
border-bottom-left-radius: pxToRem(8);
}
}
&__table-row {
display: grid;
grid-template-columns: minmax(0, var(--feature-cell-width)) repeat(var(--tier-count), 1fr);
grid-column: 1 / calc(var(--tier-count) + 2);
}
/* Enhancement for browsers that support subgrid */
@supports (grid-template-columns: subgrid) {
&__table-header,
&__table-footer {
grid-column: 1 / calc(var(--tier-count) + 2);
grid-template-columns: subgrid;
}
&__table-body {
display: grid;
grid-template-columns: subgrid;
grid-column: 1 / calc(var(--tier-count) + 2);
}
&__table-section {
grid-template-columns: subgrid;
grid-column: 1 / calc(var(--tier-count) + 2);
&-header {
grid-column: 1 / calc(var(--tier-count) + 2);
}
&-rows {
grid-template-columns: subgrid;
grid-column: 1 / calc(var(--tier-count) + 2);
}
}
&__table-row {
grid-template-columns: subgrid;
grid-column: 1 / calc(var(--tier-count) + 2);
}
}
&__table-cell {
display: flex;
align-items: center;
justify-content: center;
width: pxToRem(200);
padding: $spacer $spacer * 2;
padding: pxToRem(16) pxToRem(32);
background-color: $neutral-98;
font-size: pxToRem(14);
font-weight: 400;
line-height: 1.5;
color: $neutral-30;
border-right: 0.5px solid $neutral-80;
text-align: center;
border-bottom: 0.5px solid $neutral-80;
border-left: 0.5px solid $neutral-80;
}
&:last-child {
border-right: none;
&__table-cell--feature {
justify-content: flex-start;
text-align: left;
}
&__table-cell--highlight {
background-color: $secondary-blue-95;
}
&__table-header &__table-cell--feature,
&__table-footer &__table-cell--feature {
background-color: transparent;
}
&__table-header {
& .pricing-table__table-cell {
border-top: 0.5px solid $neutral-80;
border-bottom: none;
}
&--feature {
flex: 1;
justify-content: flex-start;
text-align: left;
}
&--tier {
padding: $spacer $spacer * 2;
& .pricing-table__table-cell:not(.pricing-table__table-cell--feature) {
padding: pxToRem(16) pxToRem(32);
font-size: pxToRem(20);
font-weight: 600;
line-height: 1.5;
letter-spacing: -0.2px;
color: $neutral-20;
background-color: $neutral-98;
border-bottom: 0.5px solid $neutral-80;
}
text-align: center;
&--highlight {
background-color: #f5f8ff;
&.pricing-table__table-cell--tier {
border-right: 0.5px solid $neutral-80;
}
}
&--cta {
padding: $spacer * 2 $spacer;
background-color: $neutral-98;
.button {
white-space: nowrap;
padding: $spacer * 0.5 $spacer * 1.375;
font-size: pxToRem(16);
&.pricing-table__table-cell--highlight {
background-color: $secondary-blue-95;
}
.button_outlined {
color: $neutral-20;
&:nth-of-type(2) {
border-top-left-radius: pxToRem(8);
}
&:last-of-type {
border-top-right-radius: pxToRem(8);
}
}
}
&__table-footer {
display: flex;
justify-content: end;
.button {
padding: $spacer * 0.5 $spacer * 1;
font-size: pxToRem(16);
height: auto;
.pricing-table__table-cell {
flex: 1;
max-width: 12.53rem;
&:first-child {
max-width: 18.74rem;
border-right: 0;
&_outlined {
color: $neutral-20;
}
&:not(:first-child) {
}
& .pricing-table__table-cell {
display: flex;
align-items: center;
justify-content: center;
border-bottom: 0.5px solid $neutral-80;
padding: pxToRem(32) 1rem;
}
& .pricing-table__table-cell:not(.pricing-table__table-cell--feature) {
background-color: $neutral-98;
text-align: center;
&.pricing-table__table-cell--highlight {
background-color: $secondary-blue-95;
}
&:nth-child(2) {
border-left: 0.5px solid $neutral-80;
&:nth-of-type(2) {
border-bottom-left-radius: pxToRem(8);
}
&:last-child {
border-right: 0.5px solid $neutral-80;
&:last-of-type {
border-bottom-right-radius: pxToRem(8);
}
}
}
&__table-header .pricing-table__table-cell:last-child,
&__table-footer .pricing-table__table-cell:last-child,
&__table-row .pricing-table__table-cell:last-child {
border-right: 0.5px solid $neutral-80;
}
&__table-header &__table-cell--feature {
border-top: none;
border-left: none;
border-bottom: none;
border-top-left-radius: pxToRem(8);
}
&__table-footer &__table-cell--feature {
border-left: none;
border-bottom: none;
border-bottom-left-radius: pxToRem(8);
}
&__table-body {
.pricing-table__table-section:last-of-type
.pricing-table__table-section-rows
.pricing-table__table-row:last-child
.pricing-table__table-cell:first-child {
border-bottom-left-radius: pxToRem(8);
}
}
&--highlight {
background-color: $secondary-blue-95;
}
&__chevron {
color: $neutral-20;
transition: transform 0.2s ease;
}
&__check {
color: $neutral-30;
}
@@ -193,7 +268,9 @@
display: none;
}
// Mobile layout
@include media-breakpoint-down(xl) {
&__table {
overflow-x: auto;
}
@@ -214,7 +291,7 @@
}
@media (min-width: 992px) and (max-width: 1999px) {
&__table-cell--cta .button {
&__table-footer .button {
white-space: normal;
text-align: center;
height: auto;
@@ -270,7 +347,8 @@
}
&__table-row {
display: flex;
display: grid;
grid-template-columns: 60% 40%;
}
&__table-cell {
@@ -281,14 +359,15 @@
&.pricing-table__table-cell--mobile-active {
display: flex;
flex: 0 0 auto;
width: pxToRem(144);
grid-column: 2;
align-items: center;
justify-content: center;
border-right: none;
}
}
&--feature {
flex: 1;
grid-column: 1;
min-width: 0;
border-radius: 0;
}
@@ -297,12 +376,15 @@
&__table-body {
.pricing-table__table-section:last-of-type
.pricing-table__table-section-rows
.pricing-table__table-row:last-child {
.pricing-table__table-row:last-child
.pricing-table__table-cell:first-child {
border-bottom-left-radius: 0;
}
}
&__table-footer {
display: grid;
grid-template-columns: 1fr;
width: 100%;
.pricing-table__table-cell--feature {
@@ -310,14 +392,12 @@
}
.pricing-table__table-cell--cta {
flex: 1 1 100%;
min-width: 100%;
display: none;
&[data-tier-col].pricing-table__table-cell--mobile-active {
display: flex;
flex: 1 1 100%;
width: 100%;
min-width: 100%;
grid-column: 1;
align-items: center;
justify-content: center;
border-left: 0.5px solid $neutral-80;
border-right: 0.5px solid $neutral-80;
@@ -328,6 +408,10 @@
}
&__table-section {
&-rows {
border-right: 0.5px solid $neutral-80;
}
&:first-of-type &-header {
border-radius: 0;
}
@@ -13,20 +13,8 @@
right: 0;
bottom: 0;
pointer-events: none;
background: url('/img/prices-background.svg') top center / 100% auto no-repeat;
}
&__grid {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 400px;
display: grid;
grid-template-columns: repeat(18, 80px);
grid-template-rows: repeat(5, 80px);
justify-content: center;
background: url('/img/prices-background.svg') center center no-repeat;
background-size: cover;
&::after {
content: '';
position: absolute;
@@ -93,8 +93,7 @@
}
&--paid {
/* Don't add full border - it doubles with neighbors; use background for emphasis */
background-color: #f5f8ff;
background-color: $secondary-blue-95;
}
}
@@ -3,7 +3,6 @@
<section class="qdrant-pricing-comparison-hero">
<div class="qdrant-pricing-comparison-hero__background">
<div class="qdrant-pricing-comparison-hero__grid"></div>
</div>
<div class="container">
<div class="qdrant-pricing-comparison-hero__content">