From b3270eb369c67b8d11f02439ecf7989b4b67c837 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Fri, 19 Dec 2025 20:33:28 -0500 Subject: [PATCH] Add CSS Grid Lanes support with toggleable shop setting Implement CSS Grid Lanes (masonry layout) for product grids with graceful fallback to standard CSS Grid for browsers that don't support it yet. - Add grid_lanes_enabled column to Shop model (default: True) - Add toggle in Shop Settings under Announcement Ribbon Settings - Update product grid templates to conditionally apply grid-lanes-enabled class - CSS uses @supports (display: grid-lanes) for progressive enhancement - Mobile (160px min) and desktop (240px min) breakpoints supported When enabled and browser supports Grid Lanes, products flow into a waterfall/masonry layout where items fill the shortest column first, creating a Pinterest-style layout that handles varying image heights. --- make_post_sell/models/shop.py | 3 ++ ...e8f1d2b4_add_grid_lanes_enabled_to_shop.py | 30 ++++++++++++ make_post_sell/static/css/common.css | 48 +++++++++++++++++-- make_post_sell/templates/home.j2 | 2 +- make_post_sell/templates/shop.j2 | 2 +- make_post_sell/templates/shop_settings.j2 | 10 ++++ make_post_sell/templates/user_purchases.j2 | 2 +- make_post_sell/views/shop.py | 16 +++++++ 8 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 make_post_sell/scripts/alembic/versions/a7c3e8f1d2b4_add_grid_lanes_enabled_to_shop.py diff --git a/make_post_sell/models/shop.py b/make_post_sell/models/shop.py index b3ae57f..2030262 100644 --- a/make_post_sell/models/shop.py +++ b/make_post_sell/models/shop.py @@ -60,6 +60,9 @@ class Shop(RBase, Base): ribbon_color_1 = Column(Unicode(32), nullable=True, default="") ribbon_color_2 = Column(Unicode(32), nullable=True, default="") + # CSS Grid Lanes (masonry layout) - enabled by default + grid_lanes_enabled = Column(Boolean, default=True) + # example: "UA-XX31XX60-3". google_analytics_id = Column(Unicode(32), nullable=True, default="") plausible_domain_name = Column(Unicode(256), nullable=True, default="") diff --git a/make_post_sell/scripts/alembic/versions/a7c3e8f1d2b4_add_grid_lanes_enabled_to_shop.py b/make_post_sell/scripts/alembic/versions/a7c3e8f1d2b4_add_grid_lanes_enabled_to_shop.py new file mode 100644 index 0000000..9a1951c --- /dev/null +++ b/make_post_sell/scripts/alembic/versions/a7c3e8f1d2b4_add_grid_lanes_enabled_to_shop.py @@ -0,0 +1,30 @@ +"""add grid_lanes_enabled to shop + +Revision ID: a7c3e8f1d2b4 +Revises: b090f873502e +Create Date: 2025-12-19 12:00:00.000000 + +""" + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "a7c3e8f1d2b4" +down_revision = "b090f873502e" +branch_labels = None +depends_on = None + + +def upgrade(): + # Add grid_lanes_enabled column to mps_shop table with default True (enabled) + op.add_column( + "mps_shop", + sa.Column("grid_lanes_enabled", sa.Boolean(), nullable=False, server_default="1"), + ) + + +def downgrade(): + # Remove grid_lanes_enabled column from mps_shop table + op.drop_column("mps_shop", "grid_lanes_enabled") diff --git a/make_post_sell/static/css/common.css b/make_post_sell/static/css/common.css index e30eeb9..c934cd8 100644 --- a/make_post_sell/static/css/common.css +++ b/make_post_sell/static/css/common.css @@ -987,12 +987,36 @@ button.cart-public-button span.lock { background-color: #a3c765; } +/* + * CSS Grid Lanes: Masonry-style layout with graceful fallback + * + * Grid Lanes is a new CSS feature that creates true masonry layouts natively. + * We use @supports to detect browser support and fall back to regular CSS Grid. + * Shop owners can toggle this via the "grid-lanes-enabled" class in settings. + * + * Fallback: Standard CSS Grid with auto-fit columns (items in rows) + * Enhanced: CSS Grid Lanes (items flow into shortest column, waterfall style) + */ + +/* Default: Standard CSS Grid layout */ section.serp { display: grid; - grid-template-columns: repeat(auto-fit, minmax(160px, 1.00fr)); + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); grid-auto-columns: max-content; grid-auto-flow: dense; - grid-gap: 4px; + gap: 4px; +} + +/* Grid Lanes: Only apply when shop has it enabled AND browser supports it */ +@supports (display: grid-lanes) { + section.serp.grid-lanes-enabled { + display: grid-lanes; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 4px; + /* item-tolerance controls sensitivity when deciding item placement */ + /* Prevents layout shuffling from minor size variations */ + item-tolerance: 1em; + } } div.serp-item { @@ -1403,7 +1427,7 @@ textarea.markup-editor-textarea { /* The SERP page grid should have bigger items since it has more room. * mobile -> desktop - * 140px -> 240px */ + * 160px -> 240px */ section.serp { grid-template-columns: repeat(auto-fit, minmax(240px, .25fr)); } @@ -1538,6 +1562,24 @@ textarea.markup-editor-textarea { padding: 20px; } } + +/* + * Desktop: Progressive enhancement for CSS Grid Lanes + * This uses nested @supports inside @media to only apply when both + * the viewport is desktop-sized AND the browser supports grid-lanes + * AND the shop has grid-lanes-enabled class set + */ +@media (min-width: 960px) { + @supports (display: grid-lanes) { + section.serp.grid-lanes-enabled { + display: grid-lanes; + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + gap: 4px; + item-tolerance: 1em; + } + } +} + div.message-ribbon { color: #ffffff; background-color: #5871ad; diff --git a/make_post_sell/templates/home.j2 b/make_post_sell/templates/home.j2 index 053510c..c8945eb 100644 --- a/make_post_sell/templates/home.j2 +++ b/make_post_sell/templates/home.j2 @@ -36,7 +36,7 @@ {% else %} {% if products %} -
+
{% for product in products %} {% if product.is_ready %} diff --git a/make_post_sell/templates/shop.j2 b/make_post_sell/templates/shop.j2 index c851018..5611946 100644 --- a/make_post_sell/templates/shop.j2 +++ b/make_post_sell/templates/shop.j2 @@ -2,7 +2,7 @@ {% block content -%} -
+
{% if products %} {% for product in products %} diff --git a/make_post_sell/templates/shop_settings.j2 b/make_post_sell/templates/shop_settings.j2 index 2e391b6..e6debdd 100644 --- a/make_post_sell/templates/shop_settings.j2 +++ b/make_post_sell/templates/shop_settings.j2 @@ -527,6 +527,16 @@

+ +
+ + +
+ When enabled, products flow into a waterfall/masonry layout where items fill the shortest column first. This creates a Pinterest-style layout that better handles varying product image heights. Requires browser support for CSS Grid Lanes. + +
+
+
diff --git a/make_post_sell/templates/user_purchases.j2 b/make_post_sell/templates/user_purchases.j2 index 904b009..9433c75 100644 --- a/make_post_sell/templates/user_purchases.j2 +++ b/make_post_sell/templates/user_purchases.j2 @@ -4,7 +4,7 @@

My Purchases

-
+
{% if products %} {% for product in products %}
diff --git a/make_post_sell/views/shop.py b/make_post_sell/views/shop.py index adfb547..c19440a 100644 --- a/make_post_sell/views/shop.py +++ b/make_post_sell/views/shop.py @@ -385,6 +385,9 @@ def shop_settings(request): # Theme settings default_theme = request.params.get("default_theme", shop.default_theme) + # Grid lanes setting (keep current value by default) + grid_lanes_enabled = shop.grid_lanes_enabled + google_analytics_id = request.params.get( "google_analytics_id", shop.google_analytics_id ) @@ -564,6 +567,19 @@ def shop_settings(request): ) request.session.flash(msg) + # Handle grid lanes setting + grid_lanes_checkbox = request.params.get("grid-lanes-checkbox", "off") + grid_lanes_enabled = checkbox_to_bool(grid_lanes_checkbox) + if shop.grid_lanes_enabled != grid_lanes_enabled: + shop.grid_lanes_enabled = grid_lanes_enabled + status = "enabled" if grid_lanes_enabled else "disabled" + request.session.flash( + ( + f"Masonry layout (Grid Lanes) {status}", + "success", + ) + ) + # Handle stripe settings form if form_section == "stripe-settings": # Handle disable action