Files
overleaf-cep/services/web/frontend/stylesheets/pages/plans-2026.scss
Tim Down 8a4b1186fc Add feature comparison table to 2026 pricing page (#32876)
* Add feature table translation strings

* Add feature comparison table config, types, and unit tests

Adds plans-features-table.mjs (8 sections, 25 rows, 5 plan columns) with
PlanTypesFeatureTable type, FeatureTableColumnHeader types, and 18 unit tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add feature comparison table template and styles

* Add logos row to 2026 pricing page

* Accessibility improvements to 2026 pricing page suggested by Claude

* Stylelint appeasement

* Fixes from Copilot review

* Remove duplicate translation string

* Appease Prettier

* Non-ideal solution for pricing features table on mobile

* Make features table same width as cards container on new pricing page

* Increase spacing between cards and features table in new pricing page

* Remove help cursor from integration icons

* Add trademark symbol to FedRAMP

Co-authored-by: Antoine Clausse <antoine.clausse@overleaf.com>

* Hide features table header cell overflow

Co-authored-by: Antoine Clausse <antoine.clausse@overleaf.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Antoine Clausse <antoine.clausse@overleaf.com>
GitOrigin-RevId: 6d6c661c9d682b86bd33886a518da25f9ab6e372
2026-04-20 08:05:10 +00:00

822 lines
20 KiB
SCSS

.plans-2026 {
padding-top: $header-height;
.plans-title-section {
padding-top: var(--spacing-15);
padding-bottom: var(--spacing-13);
}
// Period toggle switch — used only by _plans-period-toggle.pug
.plans-period-switch-wrapper {
align-items: center;
display: inline-flex;
gap: var(--spacing-04);
}
.plans-period-label {
font-size: var(--font-size-02);
font-weight: 400;
line-height: var(--line-height-02);
margin: 0;
@include media-breakpoint-up(md) {
font-size: var(--font-size-03);
line-height: var(--line-height-03);
}
}
.plans-period-switch-discount-badge {
color: var(--neutral-60);
font-size: var(--font-size-01);
}
// Card-specific period spacings — scoped to .plans-cards-section
// so plans.pug is unaffected
&[data-ol-current-plan-period='annual'] {
.plans-cards-section .card-cta > .show-for-plan-period-annual {
display: grid;
grid-row: span 2;
grid-template-rows: subgrid;
padding-block-end: 0;
}
.plans-cards-section
.card-includes-list
.card-includes-list-item.show-for-plan-period-annual {
display: flex;
}
.plans-cards-section .show-for-plan-period-annual {
padding-block-end: var(--spacing-07);
position: relative;
}
.plans-feature-table-section .show-for-plan-period-annual,
.plans-feature-table-col-header-inner .show-for-plan-period-annual {
display: block;
}
@include media-breakpoint-down(xl) {
.plans-feature-table-col-header-inner .show-for-plan-period-annual {
display: none;
}
}
}
&[data-ol-current-plan-period='monthly'] {
.plans-cards-section .card-cta > .show-for-plan-period-monthly {
display: grid;
grid-row: span 2;
grid-template-rows: subgrid;
}
.plans-cards-section
.card-includes-list
.card-includes-list-item.show-for-plan-period-monthly {
display: flex;
}
.plans-cards-section .plans-new-group-member-picker {
display: none;
}
.plans-feature-table-section .show-for-plan-period-monthly,
.plans-feature-table-col-header-inner .show-for-plan-period-monthly {
display: block;
}
@include media-breakpoint-down(xl) {
.plans-feature-table-col-header-inner .show-for-plan-period-monthly {
display: none;
}
}
}
// ─── Plan cards ──────────────────────────────────────────────────────────────
.plans-cards-section {
background-color: var(--bg-light-primary);
padding-block: var(--spacing-13);
.plans-cards-toggles {
align-items: center;
display: flex;
justify-content: space-between;
}
.plans-new-period-switcher-container {
background: none;
border-radius: 0;
gap: var(--spacing-04);
margin-bottom: 0;
margin-top: 0;
padding: 0;
position: static;
label {
font-size: var(--font-size-02);
font-weight: 400;
padding: 0;
@include media-breakpoint-up(md) {
font-size: var(--font-size-03);
}
&:hover {
background-color: transparent;
cursor: default;
}
}
}
.plans-period-switch-wrapper,
.plans-student-info-icon,
.plans-student-toggle-wrapper {
align-items: center;
display: inline-flex;
}
.plans-student-info-icon {
color: var(--content-secondary);
cursor: default;
font-size: var(--font-size-05);
}
.plans-student-toggle-wrapper {
gap: var(--spacing-04);
}
.plans-student-toggle-label-container {
position: relative;
}
&:not([data-ol-student-mode='true']) .plans-price-student,
&:not([data-ol-student-mode='true']) .show-for-plan-type-student {
display: none;
}
&[data-ol-student-mode='true'] {
.plans-new-group-member-picker,
.plans-educational-discount {
display: none;
}
.show-for-plan-type-student {
display: block;
}
.plans-card-col.show-for-plan-type-student {
display: grid;
}
.plans-card-col[data-ol-disabled-for-student='true'] {
display: none;
}
@include media-breakpoint-up(xl) {
.plans-cards-row-wrapper {
grid-auto-columns: auto;
grid-auto-flow: row;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
}
.plans-cards-row-wrapper {
display: grid;
grid-auto-flow: column;
grid-auto-columns: 90%;
grid-template-rows: 1fr auto auto auto;
column-gap: var(--spacing-04);
overflow-x: auto;
padding-top: var(--spacing-08);
-webkit-overflow-scrolling: touch;
// so the scrollbar doesn't overlap the last card's content
margin-bottom: calc(-1 * var(--spacing-05));
padding-bottom: var(--spacing-05);
@include media-breakpoint-up(md) {
grid-auto-columns: 60%;
}
@include media-breakpoint-up(lg) {
grid-auto-columns: 50%;
}
@include media-breakpoint-up(xl) {
grid-auto-columns: auto;
grid-auto-flow: row;
grid-template-columns: repeat(4, minmax(0, 1fr));
overflow-x: visible;
padding-bottom: 0;
}
}
.plans-card-col {
display: grid;
grid-row: span 4;
grid-template-rows: subgrid;
min-width: 280px;
@include media-breakpoint-up(xl) {
min-width: 0;
}
}
.price-plan-card {
background-color: var(--bg-light-primary);
border: 1px solid var(--neutral-30);
border-radius: var(--border-radius-medium);
color: var(--neutral-60);
display: grid;
grid-row: span 4;
grid-template-rows: subgrid;
position: relative;
width: 100%;
&[data-ol-plan-type='free'] {
background-color: var(--bg-light-secondary);
}
&.price-plan-card-highlighted {
border: 3px solid var(--green-50);
border-radius: var(--border-radius-medium);
outline: var(--spacing-02) solid var(--green-bright-tint-50);
outline-offset: 0;
}
.price-plan-card-badge {
@extend .mono-text;
background-color: var(--green-bright-tint-50);
border-radius: var(--border-radius-medium);
color: var(--neutral-90);
font-size: var(--font-size-03);
line-height: var(--line-height-02);
left: 50%;
padding: var(--spacing-04) var(--spacing-06);
position: absolute;
top: 0;
transform: translate(-50%, -50%);
white-space: nowrap;
}
}
.card-content-top {
display: flex;
flex-direction: column;
gap: var(--spacing-05);
padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x) var(--spacing-05);
}
.card-picker-area {
&:not(:empty) {
padding: 0 var(--bs-card-spacer-x) var(--spacing-05);
}
}
.card-cta {
align-self: start;
display: grid;
grid-row: span 2;
grid-template-rows: subgrid;
--secondary-cta-top-padding: var(--spacing-04);
// Secondary CTA (e.g. "Try for free →") spacing.
// padding-top is calculated because the a.btn inside already has top
// padding --spacing-03, while .plans-cta-plain-link has
// var(--spacing-04)
> .only-show-for-specific-plan-period > .plans-cta:nth-child(2) {
align-self: start;
padding-top: calc(var(--secondary-cta-top-padding) - var(--spacing-03));
}
// Plain-link and edu discount occupy row 4 of the PAGE subgrid.
// Edu discount lives inside the annual period wrapper (a subgrid child),
// so two levels of '>' are needed. Plain-link is a direct card-cta child.
> .plans-cta-plain-link {
align-self: start;
grid-row: 4;
padding-top: var(--secondary-cta-top-padding);
}
> .only-show-for-specific-plan-period > .plans-educational-discount {
align-self: center;
grid-row: 4;
padding-top: var(--spacing-04);
}
}
.card-eyebrow {
color: var(--green-60);
font-size: var(--font-size-05);
font-weight: 600;
}
.card-title {
color: var(--content-primary);
font-size: var(--font-size-07);
font-weight: 600;
margin-block: 0;
}
.plans-price-card-yearly-summary,
.student-plan-saving-badge {
font-size: var(--font-size-02);
line-height: var(--line-height-02);
position: absolute;
}
.card-title-suffix {
font-size: var(--font-size-03);
font-weight: 400;
}
.card-description {
color: var(--neutral-60);
margin-block: 0;
}
.card-includes-list {
display: flex;
flex-direction: column;
font-size: var(--font-size-02);
gap: var(--spacing-03);
list-style: none;
padding-left: 0;
}
.card-includes-list-item.only-show-for-specific-plan-period {
display: none;
}
.card-includes-list-heading {
color: var(--content-primary);
&::first-letter {
text-transform: uppercase;
}
}
li.card-includes-list-item {
display: flex;
align-items: baseline;
&::before {
color: var(--neutral-40);
content: '\2022';
flex-shrink: 0;
margin-right: var(--spacing-03);
}
}
.card-include-has-tooltip {
border-bottom: 1px dotted var(--content-secondary);
cursor: help;
}
.plans-cta-plain-link {
color: var(--content-secondary);
font-size: var(--font-size-02);
text-align: center;
text-decoration: underline;
&:hover {
color: var(--content-primary);
}
}
.card-footer {
background-color: transparent;
border: none;
border-radius: 0;
display: grid;
grid-row: span 2;
grid-template-rows: subgrid;
padding: 0 var(--spacing-08) var(--spacing-08);
}
.plans-educational-discount-label {
align-items: center;
color: var(--content-primary);
cursor: pointer;
display: flex;
gap: var(--spacing-03);
justify-content: center;
margin: 0;
text-align: center;
&:has(input:disabled) {
color: var(--content-secondary);
cursor: not-allowed;
opacity: 0.6;
}
}
.plans-educational-discount-checkbox {
accent-color: var(--green-50);
background-color: var(--bg-light-primary);
border: 1px solid var(--border-primary);
border-radius: var(--border-radius-base);
flex-shrink: 0;
height: 16px;
width: 16px;
&:disabled {
background-color: var(--bg-light-tertiary);
border-color: var(--bg-light-tertiary);
}
}
.plans-educational-discount-text {
cursor: help;
font-size: var(--font-size-02);
font-weight: 400;
line-height: var(--line-height-02);
text-decoration: underline dotted;
text-underline-offset: var(--spacing-01);
}
// Group member picker overrides inside cards
.plans-new-group-member-picker {
background-color: var(--bg-light-secondary);
border: 1px solid var(--neutral-20);
border-radius: var(--border-radius-medium);
padding: var(--spacing-04);
position: relative;
ul.plans-new-group-member-picker-list {
position: absolute;
left: 0;
right: 0;
z-index: 10;
}
}
}
.plans-cards-section .plans-cta .btn-ghost {
color: var(--green-50);
font-family: 'DM Mono', monospace;
font-size: var(--font-size-02);
font-weight: 500;
letter-spacing: -1px;
&:hover {
background-color: transparent;
}
}
.plans-cta-arrow {
margin-left: var(--spacing-02);
padding-bottom: 3px;
vertical-align: middle;
}
// ─── Organization logos ────────────────────────────────────────────────────────
.plans-and-pricing-section .plans-new-organizations {
padding-block: var(--spacing-13);
.plans-new-organizations-logo {
margin-top: var(--spacing-06);
}
}
// ─── Feature comparison table ──────────────────────────────────────────────
.plans-feature-table-section {
padding-top: var(--spacing-15);
}
.plans-feature-table-section .only-show-for-specific-plan-period {
display: none;
}
.plans-feature-table-heading {
color: var(--content-primary);
font-size: var(--font-size-08);
font-weight: 600;
margin-bottom: var(--spacing-04);
text-align: center;
}
.plans-feature-table-subheading {
color: var(--content-secondary);
font-size: var(--font-size-05);
margin-bottom: var(--spacing-10);
text-align: center;
}
.plans-feature-table-scroll-wrapper {
padding-top: var(--spacing-04);
@include media-breakpoint-down(lg) {
overflow-x: auto;
}
}
// Below xl the plan cards already show prices and CTAs — the table
// just needs plan names so users can compare features.
@include media-breakpoint-down(xl) {
.plans-feature-table-most-popular-badge {
display: none;
}
.plans-feature-table-col-header-inner .plans-cta {
display: none;
}
.plans-feature-table-col-header {
padding-bottom: var(--spacing-06);
}
}
.plans-feature-table {
border: 1px solid var(--border-divider);
// separate + spacing 0 is required for position: sticky on header cells
border-collapse: separate;
border-radius: var(--border-radius-medium);
border-spacing: 0;
min-width: 640px;
table-layout: fixed;
width: 100%;
th.plans-feature-table-corner-cell,
th.plans-feature-table-feature-name-cell {
width: 22%;
}
.plans-feature-table-header-row th:first-child {
border-top-left-radius: var(--border-radius-medium);
}
.plans-feature-table-header-row th:last-child {
border-top-right-radius: var(--border-radius-medium);
}
.plans-feature-table-section:last-child
.plans-feature-table-feature-row:last-child
th:first-child {
border-bottom-left-radius: var(--border-radius-medium);
}
.plans-feature-table-section:last-child
.plans-feature-table-feature-row:last-child
td:last-child {
border-bottom-right-radius: var(--border-radius-medium);
}
.plans-feature-table-section:last-child
.plans-feature-table-feature-row:last-child
th,
.plans-feature-table-section:last-child
.plans-feature-table-feature-row:last-child
td {
border-bottom: none;
}
}
.plans-feature-table-thead .plans-feature-table-header-row th {
backdrop-filter: blur(12px);
background: rgb($white, 0.7);
// -1px spread prevents shadow extending past table edges
box-shadow: 0 2px 4px -1px rgb($neutral-90, 0.16);
position: sticky;
top: 0;
z-index: 2;
}
.plans-feature-table-col-header {
overflow: hidden;
padding: var(--spacing-06);
// Extra bottom padding reserves space for the absolutely-positioned CTA button
padding-bottom: 56px;
text-align: center;
vertical-align: top;
}
.plans-feature-table-col-header-inner .plans-cta {
bottom: var(--spacing-06);
left: var(--spacing-06);
position: absolute;
right: var(--spacing-06);
.btn {
width: 100%;
}
}
.plans-feature-table-corner-cell {
padding: var(--spacing-06);
}
.plans-feature-table-most-popular-badge {
@extend .mono-text;
background-color: var(--green-bright-tint-50);
border-radius: var(--border-radius-medium);
color: var(--content-primary);
font-size: var(--font-size-01);
left: 50%;
padding: var(--spacing-01) var(--spacing-03);
position: absolute;
top: 0;
transform: translate(-50%, -50%);
white-space: nowrap;
z-index: 1;
}
.plans-feature-table-col-name {
color: var(--green-60);
display: block;
font-size: var(--font-size-03);
font-weight: 500;
margin-bottom: var(--spacing-02);
text-align: center;
}
.plans-feature-table-col-price {
align-items: baseline;
display: flex;
gap: var(--spacing-01);
justify-content: center;
margin-bottom: var(--spacing-02);
}
.plans-feature-table-col-price-amount {
color: var(--content-primary);
font-size: var(--font-size-03);
font-weight: 400;
}
.plans-feature-table-col-price-term {
color: var(--content-primary);
font-size: var(--font-size-03);
}
.plans-feature-table-col-price-yearly {
color: var(--neutral-60);
font-size: var(--font-size-01);
margin-bottom: var(--spacing-02);
text-align: center;
}
.plans-feature-table-section-header-row {
.plans-feature-table-section-title {
background-color: var(--bg-light-secondary);
color: var(--content-secondary);
font-size: var(--font-size-03);
font-weight: 700;
padding: var(--spacing-06);
}
.plans-feature-table-section-title-content {
display: flex;
flex-direction: column;
gap: var(--spacing-03);
}
}
.plans-feature-table-section-icons {
align-items: center;
display: flex;
gap: var(--spacing-04);
}
.plans-feature-table-section-icon-wrapper {
display: inline-flex;
}
.plans-feature-table-section-icon {
height: var(--spacing-08);
width: var(--spacing-08);
}
.plans-feature-table-feature-row th,
.plans-feature-table-feature-row td {
border-bottom: 1px solid var(--border-divider);
}
.plans-feature-table-feature-name-cell {
padding: var(--spacing-06);
vertical-align: middle;
}
.plans-feature-table-feature-name-content {
display: inline-flex;
}
.plans-feature-table-integrations-content {
display: flex;
flex-direction: column;
gap: var(--spacing-03);
}
.plans-feature-table-integrations-label {
color: var(--content-secondary);
font-size: var(--font-size-02);
font-weight: 500;
}
.plans-feature-table-feature-name {
color: var(--neutral-60);
font-size: var(--font-size-03);
font-weight: 400;
&.plans-feature-table-feature-name-has-tooltip {
cursor: help;
text-decoration: underline dotted var(--green-60);
}
}
.plans-feature-table-cell {
padding: var(--spacing-06);
text-align: center;
vertical-align: middle;
}
.plans-feature-table-check-icon {
display: inline-block;
height: var(--spacing-07);
vertical-align: middle;
width: var(--spacing-07);
}
.plans-feature-table-cell-bool-content {
display: inline-block;
position: relative;
&[data-bs-toggle='tooltip'] {
cursor: help;
}
}
.plans-feature-table-info-icon {
color: var(--content-secondary);
font-size: var(--font-size-03);
vertical-align: middle;
}
// In bool cells the info icon floats beside the check; in text cells it flows inline
.plans-feature-table-cell-bool-content .plans-feature-table-info-icon {
left: calc(100% + var(--spacing-01));
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.plans-feature-table-cell-text-content {
align-items: center;
color: var(--content-primary);
display: inline-flex;
font-size: var(--font-size-02);
gap: var(--spacing-02);
}
// ─── Quotes and FAQs section────────────────────────────────────────────────
.plans-quotes-section {
padding-bottom: var(--spacing-13);
}
.plans-faq {
padding-block: var(--spacing-15);
.faq-heading-container {
text-align: center;
margin-bottom: var(--spacing-10);
@include media-breakpoint-up(md) {
margin-bottom: var(--spacing-15);
}
h2 {
margin-bottom: 0;
}
}
.eyebrow-text {
margin-bottom: var(--spacing-08);
}
}
.plans-faq-tabs {
.nav-tabs-container {
padding-bottom: var(--spacing-05);
margin-bottom: var(--spacing-08);
ul.nav-tabs {
border-bottom-width: 2px;
font-size: var(--font-size-04);
margin: unset;
justify-content: space-between;
}
}
}
}