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