From dfb70666b45a67234a33db9b8549f00b838861c4 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Sun, 15 Feb 2026 05:25:30 -0500 Subject: [PATCH] Add grayscale mode: CSS filter renders entire shop in black and white MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Server renders data-grayscale="on" on so it works without JS. Toggle in Ribbon & Layout settings, off by default. Original assets untouched — purely presentational via filter: grayscale(100%). --- make_post_sell/models/shop.py | 3 + ...c3b37cb2c9cc_add_grayscale_mode_to_shop.py | 36 ++++++++++ make_post_sell/static/css/common.css | 6 ++ make_post_sell/templates/base.j2 | 2 +- make_post_sell/templates/shop_settings.j2 | 15 ++++ make_post_sell/tests/test_functional.py | 70 +++++++++++++++++++ make_post_sell/views/shop.py | 14 ++++ 7 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 make_post_sell/scripts/alembic/versions/c3b37cb2c9cc_add_grayscale_mode_to_shop.py diff --git a/make_post_sell/models/shop.py b/make_post_sell/models/shop.py index 69437a0..e18a601 100644 --- a/make_post_sell/models/shop.py +++ b/make_post_sell/models/shop.py @@ -136,6 +136,9 @@ class Shop(RBase, Base): # Watch mode: sticky video, autoplay, related content sidebar watch_mode_enabled = Column(Boolean, default=False) + # Grayscale mode: render entire site in black and white + grayscale_mode = Column(Boolean, default=False) + # Email digest subscriptions subscriptions_enabled = Column(Boolean, default=True) diff --git a/make_post_sell/scripts/alembic/versions/c3b37cb2c9cc_add_grayscale_mode_to_shop.py b/make_post_sell/scripts/alembic/versions/c3b37cb2c9cc_add_grayscale_mode_to_shop.py new file mode 100644 index 0000000..3daf015 --- /dev/null +++ b/make_post_sell/scripts/alembic/versions/c3b37cb2c9cc_add_grayscale_mode_to_shop.py @@ -0,0 +1,36 @@ +"""add grayscale_mode to shop + +Revision ID: c3b37cb2c9cc +Revises: 3c0996ae20f3 +Create Date: 2026-02-14 20:13:18.055029 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'c3b37cb2c9cc' +down_revision = '3c0996ae20f3' +branch_labels = None +depends_on = None + +from make_post_sell.models.meta import UUIDType + + +def _column_exists(table, column): + conn = op.get_bind() + result = conn.execute(sa.text(f"PRAGMA table_info({table})")) + return any(row[1] == column for row in result.fetchall()) + + +def upgrade(): + if not _column_exists("mps_shop", "grayscale_mode"): + op.add_column( + "mps_shop", + sa.Column("grayscale_mode", sa.Boolean(), nullable=False, server_default="0"), + ) + + +def downgrade(): + op.drop_column("mps_shop", "grayscale_mode") diff --git a/make_post_sell/static/css/common.css b/make_post_sell/static/css/common.css index 2e1eac3..1ff01aa 100644 --- a/make_post_sell/static/css/common.css +++ b/make_post_sell/static/css/common.css @@ -3396,3 +3396,9 @@ textarea { .price-history-current td { font-weight: bold; } + +/* Grayscale mode */ +html[data-grayscale="on"] { + filter: grayscale(100%); + -webkit-filter: grayscale(100%); +} diff --git a/make_post_sell/templates/base.j2 b/make_post_sell/templates/base.j2 index 69e72c6..dbb4c37 100644 --- a/make_post_sell/templates/base.j2 +++ b/make_post_sell/templates/base.j2 @@ -14,7 +14,7 @@ {% else %} {% set theme_preference = 'light' if (shop_default_theme == 1) else 'dark' %} {% endif %} - + diff --git a/make_post_sell/templates/shop_settings.j2 b/make_post_sell/templates/shop_settings.j2 index ea01f5e..42b0963 100644 --- a/make_post_sell/templates/shop_settings.j2 +++ b/make_post_sell/templates/shop_settings.j2 @@ -755,6 +755,21 @@

+ +
+ + +
+ + +
+ When enabled, every element on the site — images, video, text, and UI — renders in black and white. Original files are not modified. + +
+
+
diff --git a/make_post_sell/tests/test_functional.py b/make_post_sell/tests/test_functional.py index 07b5924..8cb582b 100644 --- a/make_post_sell/tests/test_functional.py +++ b/make_post_sell/tests/test_functional.py @@ -3604,3 +3604,73 @@ class TestAnalytics(AuthenticatedFunctionalTests): self.assertIn("Price History", body) self.assertIn("$20.00", body) self.assertIn("$3.50", body) + + def test_grayscale_mode_default_off(self): + """Test that new shops have grayscale mode disabled by default.""" + shop = self._create_shop_helper( + user_creds=self.user1_creds, shop_params=self.shop1_params + ) + + self.dbsession.refresh(shop) + self.assertFalse(shop.grayscale_mode) + + def test_grayscale_mode_toggle(self): + """Test enabling and disabling grayscale mode via settings form.""" + shop = self._create_shop_helper( + user_creds=self.user1_creds, shop_params=self.shop1_params + ) + + # Enable grayscale mode + res = self.testapp.post( + f"/s/{shop.id}/settings", + { + "form_section": "ribbon-settings", + "grayscale_mode": "1", + "submit": "Save Settings", + }, + status=302, + ) + res = res.follow() + + self.dbsession.expire(shop) + self.assertTrue(shop.grayscale_mode) + + # Disable grayscale mode + res = self.testapp.post( + f"/s/{shop.id}/settings", + { + "form_section": "ribbon-settings", + "grayscale_mode": "0", + "submit": "Save Settings", + }, + status=302, + ) + res = res.follow() + + self.dbsession.expire(shop) + self.assertFalse(shop.grayscale_mode) + + def test_grayscale_mode_html_attribute(self): + """Test that data-grayscale attribute appears in HTML when enabled.""" + shop = self._create_shop_helper( + user_creds=self.user1_creds, shop_params=self.shop1_params + ) + + # Without grayscale, attribute should not be present + res = self.testapp.get(f"/s/{shop.id}/{shop.slug}", status=200) + self.assertNotIn('data-grayscale="on"', res.text) + + # Enable grayscale mode + self.testapp.post( + f"/s/{shop.id}/settings", + { + "form_section": "ribbon-settings", + "grayscale_mode": "1", + "submit": "Save Settings", + }, + status=302, + ) + + # With grayscale, attribute should be present + res = self.testapp.get(f"/s/{shop.id}/{shop.slug}", status=200) + self.assertIn('data-grayscale="on"', res.text) diff --git a/make_post_sell/views/shop.py b/make_post_sell/views/shop.py index 7da932c..a37c317 100644 --- a/make_post_sell/views/shop.py +++ b/make_post_sell/views/shop.py @@ -624,6 +624,20 @@ def shop_settings(request): ) ) + # Handle grayscale mode setting + grayscale_mode_value = request.params.get("grayscale_mode") + if grayscale_mode_value is not None: + grayscale_mode = int(grayscale_mode_value) == 1 + if shop.grayscale_mode != grayscale_mode: + shop.grayscale_mode = grayscale_mode + status = "enabled" if grayscale_mode else "disabled" + request.session.flash( + ( + f"Grayscale mode {status}", + "success", + ) + ) + # Handle stripe settings form if form_section == "stripe-settings": # Handle disable action