Add grayscale mode: CSS filter renders entire shop in black and white
Server renders data-grayscale="on" on <html> so it works without JS. Toggle in Ribbon & Layout settings, off by default. Original assets untouched — purely presentational via filter: grayscale(100%).
This commit is contained in:
parent
d07195568b
commit
dfb70666b4
7 changed files with 145 additions and 1 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
@ -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%);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
{% else %}
|
||||
{% set theme_preference = 'light' if (shop_default_theme == 1) else 'dark' %}
|
||||
{% endif %}
|
||||
<html lang="en" data-theme="{{ theme_preference }}" data-user-theme="{{ user_theme_id or 'null' }}" data-shop-default="{{ shop_default_theme }}">
|
||||
<html lang="en" data-theme="{{ theme_preference }}" data-user-theme="{{ user_theme_id or 'null' }}" data-shop-default="{{ shop_default_theme }}"{% if request.shop and request.shop.grayscale_mode %} data-grayscale="on"{% endif %}>
|
||||
<head>
|
||||
|
||||
<!-- stylesheet to-end-all stylesheets, pico.fluid.classless.css ! woot -->
|
||||
|
|
|
|||
|
|
@ -755,6 +755,21 @@
|
|||
<br />
|
||||
<br />
|
||||
|
||||
<label>Grayscale Mode</label>
|
||||
<br />
|
||||
<input type="radio" name="grayscale_mode" id="grayscale_mode_on" value="1"
|
||||
{% if request.shop.grayscale_mode %}checked{% endif %} />
|
||||
<label for="grayscale_mode_on" class="inline-label">Enable grayscale (render entire site in black and white)</label>
|
||||
<br />
|
||||
<input type="radio" name="grayscale_mode" id="grayscale_mode_off" value="0"
|
||||
{% if not request.shop.grayscale_mode %}checked{% endif %} />
|
||||
<label for="grayscale_mode_off" class="inline-label">Disable grayscale</label>
|
||||
<br />
|
||||
<small class="note-text">When enabled, every element on the site — images, video, text, and UI — renders in black and white. Original files are not modified.</small>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<input type="submit" name="submit" class="mps-submit" value="Save Settings" />
|
||||
|
||||
<br />
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue