Skip to content

Latest commit

Β 

History

837 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Swissup Breeze Theme Editor

Version License PHP

Visual theme customization tool for Magento 2 Breeze themes with live preview, draft/publish workflow, and 16+ field types.

πŸ“š Documentation

Full documentation: https://docs.swissuplabs.com/m2/extensions/breeze-theme-editor/

✨ Features

  • Live Preview - See changes instantly without page reload
  • Draft/Publish Workflow - Test before going live
  • 16+ Field Types - Colors, fonts, toggles, images, spacing, repeaters, and more
  • Dual Color Format Support - RGB (Breeze 2.0) and HEX (Breeze 3.0) with auto-detection
  • Theme Inheritance - Extend parent theme configurations
  • Multi-Store Support - Different settings per store view
  • Version Control - Publication history with rollback
  • Secure Access - Token-based authentication (3-hour sessions)

πŸš€ Quick Start for Theme Developers

1. Create Configuration File

Add etc/theme_editor/settings.json to your theme:

{
  "version": "1.0",
  "sections": [
    {
      "id": "colors",
      "name": "Colors",
      "icon": "palette",
      "settings": [
        {
          "id": "primary_color",
          "label": "Primary Color",
          "type": "color",
          "default": "#1979c3",
          "css_var": "--primary-color",
          "description": "Main brand color"
        },
        {
          "id": "text_color",
          "label": "Text Color",
          "type": "color",
          "default": "#333333",
          "css_var": "--text-color",
          "description": "Body text color"
        }
      ]
    },
    {
      "id": "typography",
      "name": "Typography",
      "icon": "text_fields",
      "settings": [
        {
          "id": "body_font",
          "label": "Body Font",
          "type": "font_picker",
          "default": "Open Sans",
          "css_var": "--font-family-base",
          "options": [
            {"value": "Open Sans", "label": "Open Sans"},
            {"value": "Roboto", "label": "Roboto"},
            {"value": "Georgia", "label": "Georgia"}
          ]
        },
        {
          "id": "font_size",
          "label": "Base Font Size",
          "type": "number",
          "default": "16",
          "min": "12",
          "max": "24",
          "step": "1",
          "css_var": "--font-size-base",
          "description": "Base text size in pixels"
        }
      ]
    },
    {
      "id": "layout",
      "name": "Layout",
      "icon": "view_quilt",
      "settings": [
        {
          "id": "container_width",
          "label": "Container Width",
          "type": "text",
          "default": "1280px",
          "css_var": "--container-width",
          "placeholder": "e.g. 1280px"
        },
        {
          "id": "sticky_header",
          "label": "Sticky Header",
          "type": "toggle",
          "default": true,
          "css_var": "--header-sticky",
          "description": "Fix header on scroll"
        }
      ]
    }
  ]
}

2. Use CSS Variables in Your Theme

The Theme Editor generates CSS variables that you can use in your stylesheets:

/* web/css/source/_theme.less or styles.css */

:root {
  /* These will be overridden by Theme Editor */
  --primary-color: 25, 121, 195;  /* RGB format for Breeze */
  --text-color: 51, 51, 51;
  --font-family-base: "Open Sans", sans-serif;
  --font-size-base: 16;
  --container-width: 1280px;
  --header-sticky: 1;
}

/* Use variables in your styles */
.btn-primary {
  background: rgb(var(--primary-color));
}

body {
  color: rgb(var(--text-color));
  font-family: var(--font-family-base);
  font-size: calc(var(--font-size-base) * 1px);
}

.page-wrapper {
  max-width: var(--container-width);
  margin: 0 auto;
}

.page-header {
  position: var(--header-sticky) == 1 ? sticky : relative;
}

3. Field Types Quick Reference

Type Description CSS Output Example
color Color picker with RGB/HEX support --color: 255, 0, 0 (RGB) or --color: #ff0000 (HEX)
text Single line input --width: 1280px
textarea Multi-line text input --content: "text..."
number Numeric input --columns: 4
range Slider control --opacity: 0.8
select Dropdown selector --variant: primary
checkbox Multiple selection JSON data
toggle On/Off switch --enabled: 1 or 0
font_picker Font selector --font: "Georgia", serif
color_scheme Predefined color schemes Multiple CSS vars
icon_set_picker Icon set selector --icon-set: fontawesome
spacing 4-sided control --padding: 20px or 10px 20px
image_upload Image upload --logo: url(...)
code Code editor CSS/JS code
social_links Social media links JSON data
repeater Dynamic lists JSON data for JS

Color Format Support:

  • Breeze 2.0 themes use RGB format: --color: 255, 0, 0
  • Breeze 3.0 themes use HEX format: --color: #ff0000
  • Format is auto-detected from default value or set explicitly via format field

See full documentation: https://docs.swissuplabs.com/m2/extensions/breeze-theme-editor/theme-developer-guide/

πŸ”Œ GraphQL API Example

The module provides a comprehensive GraphQL API for programmatic access:

query {
  breezeThemeEditorConfig(storeId: 1, status: DRAFT) {
    version
    sections {
      code
      label
      fields {
        code
        label
        type
        value
        default
        format
        cssVar
      }
    }
    palettes {
      id
      label
      groups {
        colors {
          id
          label
          value
          cssVar
          format
        }
      }
    }
  }
}

API Features:

  • 10 Query operations (config, values, publications, presets, etc.)
  • 9 Mutation operations (save, publish, rollback, import/export, etc.)
  • Full support for draft/publish workflow
  • Publication history with changelog tracking

See full API documentation: https://docs.swissuplabs.com/m2/extensions/breeze-theme-editor/graphql-api/

🎨 Color Format Support (Breeze 2.0 vs 3.0)

The Theme Editor supports both RGB and HEX color formats to ensure compatibility with different Breeze versions:

RGB Format (Breeze 2.0)

{
  "id": "primary_color",
  "type": "color",
  "default": "25, 121, 195",
  "format": "rgb",
  "css_var": "--primary-color"
}

CSS output: --primary-color: 25, 121, 195

Usage: background: rgb(var(--primary-color))

HEX Format (Breeze 3.0)

{
  "id": "primary_color",
  "type": "color",
  "default": "#1979c3",
  "format": "hex",
  "css_var": "--primary-color"
}

CSS output: --primary-color: #1979c3

Usage: background: var(--primary-color)

Auto-Detection

If format is not specified, the Theme Editor automatically detects the format from the default value:

  • Contains comma β†’ RGB format
  • Starts with # β†’ HEX format
  • Contains rgb() wrapper β†’ RGB format (wrapper is removed)

Note: The GraphQL API includes the format field for all color values.

4. Enable and Access

  1. Enable in Admin:

    • Go to: Stores > Configuration > Swissup > Breeze Theme Editor
    • Select Yes and save
  2. Access Theme Editor:

    • Click "Open Frontend in Theme Editor Mode" button in admin config
    • Or use URL: https://your-store.com/?breeze_theme_editor_access_token=YOUR_TOKEN
    • Access token is valid for 3 hours
  3. Make Changes:

    • Panel appears on the right side
    • Edit values with live preview
    • Click Save Draft or Publish

πŸ” Sites Behind HTTP Basic Auth

On staging sites protected by HTTP Basic Auth (.htaccess), the browser keeps asking for the password over and over as soon as the Theme Editor opens.

Why: the admin UI authenticates its GraphQL calls with Authorization: Bearer <token>. Apache/nginx consumes that header first, tries to read it as Basic credentials, fails, and answers 401 WWW-Authenticate: Basic before Magento is ever reached. Every XHR then triggers a native password prompt, and no correct password can clear it.

Fix (no server access needed): move the token to another header.

Go to Stores > Configuration > Swissup > Breeze Theme Editor > General Settings and set GraphQL Authorization Header to X-Bte-Authorization.

The admin JS then sends the token there, Basic Auth ignores the unknown header, and the module copies the value into Authorization server-side, right before Magento validates the token. Authentication itself is unchanged β€” the same JWT, the same core validator, the same ACL checks.

Same thing from the CLI:

php bin/magento config:set breeze_theme_editor/general/auth_header X-Bte-Authorization
php bin/magento cache:flush

Alternative fix (requires server access): let Bearer requests through Basic Auth β€” for the GraphQL endpoint only.

⚠️ Do not apply these rules site-wide. The web server can only check that the header starts with Bearer, not that the token is valid, so an unscoped rule lets anyone bypass Basic Auth on every route by sending an arbitrary Bearer value. Scoped to the GraphQL endpoint, the exception is limited to a path Magento authenticates itself β€” but note that /graphql is a public API in Magento, so its unauthenticated queries (catalog data and the like) become reachable on the staging site.

πŸ“ Substitute your actual endpoint path. The module builds the endpoint from the store base URL, so a subdirectory install answers on /shop/graphql, not /graphql. Every /graphql below must be replaced with your real path β€” the 401 message the Theme Editor shows prints the path it is actually calling. Keep the patterns anchored, and escape regex metacharacters if your base path contains any (/shop.v2/graphql β†’ ^/shop\.v2/graphql$).

Apache β€” in the vhost, where <LocationMatch> matches the URL as it arrived, before any rewriting. Use <LocationMatch> with an anchored pattern rather than <Location>: <Location> matches by prefix, so /graphql would also cover unrelated routes such as /graphql-admin.

<LocationMatch "^/graphql$">
    SetEnvIf Authorization "^Bearer " BTE_BEARER
    <RequireAny>
        Require env BTE_BEARER
        Require valid-user
    </RequireAny>
</LocationMatch>

<LocationMatch> is not allowed in .htaccess. There, use <If> β€” but verify it with the curl check below, because per-directory configuration is merged after Magento's rewrite to index.php on some setups:

<If "%{REQUEST_URI} =~ m#^/graphql$#">
    SetEnvIf Authorization "^Bearer " BTE_BEARER
    <RequireAny>
        Require env BTE_BEARER
        Require valid-user
    </RequireAny>
</If>

nginx β€” the exception has to be decided at server level, not inside a location. Magento's routing internally redirects /graphql to index.php, and the redirected request re-enters the PHP location, which inherits the server-level auth_basic and challenges again. $request_uri keeps the original URI across that redirect, so key the realm on it:

# http { } block
# $request_uri carries the query string, so match the bare path or the path
# followed by "?" β€” and anchor it, or /graphql-admin would opt out too.
map $request_uri $bte_uri_ok {
    default          0;
    "~^/graphql$"    1;
    "~^/graphql\?"   1;
}

map $http_authorization $bte_bearer_ok {
    default       0;
    "~*^Bearer "  1;
}

map "$bte_uri_ok$bte_bearer_ok" $bte_realm {
    default  "restricted";
    "11"     off;
}

# server { } block β€” applies to every location, PHP included
auth_basic           $bte_realm;
auth_basic_user_file /path/to/.htpasswd;

Verify with:

curl -s -o /dev/null -D - -X POST https://your-store.com/graphql \
  -H 'Authorization: Bearer test' \
  -H 'Content-Type: application/json' \
  -d '{"query":"{__typename}"}'

A 401 with www-authenticate: Basic means Basic Auth is still intercepting; anything else means the request reaches Magento.

Then check that a normal page still asks for the password:

curl -s -o /dev/null -D - https://your-store.com/ -H 'Authorization: Bearer test'

This must still return 401. If it returns 200, the rule was applied too broadly and the whole site is now reachable with an arbitrary Bearer header.

The nginx recipe above was verified against nginx 1.24 with Basic Auth enabled and Magento at the web root:

request result
/graphql with a valid Bearer token reaches Magento, authenticated
/graphql?x=1 with a valid Bearer token reaches Magento, authenticated
/graphql-admin with a Bearer header Basic challenge β€” not bypassed
/GraphQL with a Bearer header Basic challenge β€” not bypassed
/ with a Bearer header Basic challenge β€” not bypassed
/graphql with no auth header Basic challenge β€” not bypassed

The Apache variants follow the same idea but were not tested here β€” run both curl checks after applying them.

πŸ“¦ Installation

Via Composer (Recommended)

composer require swissup/module-breeze-theme-editor
php bin/magento module:enable Swissup_BreezeThemeEditor
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy
php bin/magento cache:flush

Manual Installation

  1. Download the module
  2. Extract files to app/code/Swissup/BreezeThemeEditor
  3. Run the following commands:
php bin/magento module:enable Swissup_BreezeThemeEditor
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy
php bin/magento cache:flush

πŸ§ͺ Running Tests

This module includes comprehensive test coverage:

  • Backend Tests: 25+ PHPUnit tests for PHP code
  • Frontend Tests: 24 JavaScript tests for UI components

Backend PHP Tests

Using Docker (Recommended):

cd /path/to/magento/root/src
bin/clinotty bash -c "cd vendor/swissup/module-breeze-theme-editor && ../../bin/phpunit"

Using PHPUnit directly:

cd vendor/swissup/module-breeze-theme-editor
../../bin/phpunit

Run specific test file:

../../bin/phpunit Test/Unit/Model/Service/CssGeneratorTest.php

Frontend JavaScript Tests

Access the browser-based test runner:

http://your-store.com/?jstest=1&autorun=1

URL Parameters:

  • jstest=1 - Enable test mode
  • autorun=1 - Automatically run all tests on page load
  • suite=<name> - Run specific test suite only

For complete testing documentation, see README-TESTS.md

πŸ“‹ Requirements

  • Magento 2.4.x or higher
  • PHP 7.4, 8.0, 8.1, or 8.2
  • Breeze Frontend (v2.0+ for RGB format, v3.0+ for HEX format)

🀝 Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new features
  4. Submit a pull request

πŸ“„ License

OSL-3.0

πŸ’¬ Support

πŸ”— Links

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages