Linux 软件免费装
Banner图

Theme SCSS Compiler

开发者 simonmista
更新时间 2026年8月22日 19:08
PHP版本: 8.1 及以上
WordPress版本: 7.1
版权: GPLv2 or later
版权网址: 版权信息

标签

css theme sass compiler scss

下载

1.0.2 1.0.4 1.0.5 1.0.0 1.0.6 1.0.1 1.0.3 1.0.7 1.0.8

详情介绍:

Your theme has a .scss file. Your site needs a .css file. Theme SCSS Compiler does that one conversion, on your server, from Tools → Theme SCSS Compiler. You give it a source and a target, for example assets/scss/style.scss and assets/css/style.css. Save the SCSS, open any admin page, and the CSS is rebuilt. No Node.js, no npm install, no Gulp task, no CI step: the Sass compiler (scssphp) ships inside the plugin. SCSS is the Sass syntax that looks like CSS. Every valid CSS file is already valid SCSS, and you get nesting, variables, mixins and @import on top. It is built for the case that keeps coming up: a theme with one or two stylesheets, hosting where a build pipeline is not going to happen, and someone who wants to change a variable and see the result. Features Scope The whole plugin works inside one theme, the active one. Both paths of a pair resolve against it, and that is also the write boundary: a path containing .. is refused, and nothing is ever written outside the theme. An @import inside your SCSS can still read a file elsewhere on the server, and output built from one is refused before it reaches disk. With a child theme active, the child is that theme. That keeps the tool small on purpose. One theme, its stylesheets, nothing else to configure. Where the compiled CSS goes Compiling writes into your theme. It writes the CSS target of each pair, plus a short-lived temporary file beside it that is renamed onto the target when the compile finishes, and it creates the directory for the target if it is missing. Nothing else in the theme is touched: sources are only read, and a target that is not a .css file is refused before the write, so a mistyped path cannot land on functions.php. Commit those CSS files or add them to .gitignore. Both work, because auto-compile rebuilds anything missing. Privacy No external HTTP requests, no cookies, no telemetry, no tracking. Everything happens on your server. The plugin bundles scssphp and its dependencies (league/uri, scssphp/source-span, symfony/filesystem, the symfony ctype and mbstring polyfills, and the PSR HTTP interfaces). All are MIT licensed and GPL-compatible, and their source sits in the plugin's vendor/ directory.

安装:

  1. Upload the theme-scss-compiler folder to /wp-content/plugins/, or install the ZIP through Plugins → Add New.
  2. Activate it.
  3. Open Tools → Theme SCSS Compiler and set the SCSS source, the CSS target and the context for each pair.
  4. Click Save settings. Compiling builds the saved configuration, so Compile now stays locked while there are unsaved changes.
  5. Click Compile now. After that, saving a SCSS file and loading any admin page is enough.
If your theme has no assets/scss/style.scss, replace the default pair in step 3 with your own paths. Auto-compile skips a pair whose source does not exist; Compile now reports the missing source. Configuration can also live in code. See the FAQ entries on TSCSSCOMPILER_PAIRS.

屏幕截图:

  • Auto-recompile switched on. While the settings page is open, a countdown re-checks the sources and rebuilds only what actually changed, and the button underneath pauses it. Off by default.

升级注意事项:

1.0.8 Constants now work one at a time: a lone define() finally takes effect and locks just that setting, so check any you already have. The bundled German translation now loads everywhere. Atomic CSS writes, an accessibility pass, tested with WordPress 7.1. 1.0.7 Adds an optional auto-recompile timer on the settings page (off by default), reorganises the settings into clearer cards, and locks Compile now until your changes are saved. 1.0.6 Lists frontend and admin theme stylesheets in the status panel, hardens SCSS @import handling, and fixes a false compile-error notice – recommended for everyone. 1.0.5 Fixes a fatal error when compiling on servers that fall back to the FTP/SSH filesystem (often nginx/php-fpm). Recommended for everyone; required if "Compile now" crashed with an ftp_nlist error. 1.0.4 Required on PHP 8.1: fixes a fatal error that stopped the plugin from loading. PHP 8.2+ is unaffected. Also adds a Settings link on the Plugins screen and fixes input alignment on WordPress 7. 1.0.3 Compatibility update: tested with WordPress 7.0. No functional changes. 1.0.2 "Compile now" refreshes changed Version fields without a reload, plus expanded docs. 1.0.1 Bug-fix release. Recommended for everyone using auto-compile, and required if your theme directory is a symlink (e.g. Bedrock) – without this fix auto-recompile of changed @import partials does not work in that setup. 1.0.0 Initial release.

常见问题:

Do I need Node.js, npm or a build step?

No. The compiler is PHP and ships with the plugin. Nothing is installed on the server, nothing runs on your machine. You need WordPress 6.3 and PHP 8.1 or newer.

Which Sass features work?

Everything you reach for in a theme stylesheet: nesting, the & parent selector, variables, mixins, functions, @extend, @media nesting, arithmetic, interpolation, and @import for splitting your styles into partials. Import chains nest as deep as you like, and every file in the chain is tracked for change detection. Partials are the @import kind, so _variables.scss is pulled in with @import "variables"; at the top of your entry file, and everything it declares is available below. The compiler is a PHP port of Sass rather than Dart Sass, so it is worth a look at the compiled output the first time you move an existing theme over.

How do I split my styles into partials and share variables?

Give a partial a leading underscore and pull it in with @import at the top of your entry file. Everything a partial declares is available to everything below it. // assets/scss/_variables.scss $brand: #0073aa; $text: #1f232b; $radius: 8px; // assets/scss/_card.scss .card { color: $text; border-radius: $radius; } // assets/scss/_menu.scss .menu a { color: $brand; } // assets/scss/style.scss — the file you set as the pair's SCSS source @import "variables"; @import "card"; @import "menu"; Note the import name drops both the underscore and the extension: the file _card.scss is imported as "card". Two things follow from this, and both are easier to get right if you know them up front. Order matters. A partial is read at the position where you import it, so @import "variables" has to come before the partials that use those variables. Variables first, the rest below. One shared scope. Every partial writes into the same namespace, so two partials that each declare $padding will quietly disagree — last one wins. Prefix instead: $card-padding, $menu-padding. Same for mixin names. When the list gets long, collect it in one index partial and import just that: // assets/scss/_index.scss @import "variables"; @import "card"; @import "menu"; // assets/scss/style.scss @import "index"; Chains nest as deep as you like. The plugin records every file that was read at any depth, so editing _card.scss two levels down still triggers the rebuild on your next admin page load.

I am new to SCSS. What does it actually look like?

Every valid CSS file is already valid SCSS, so you can rename style.css to style.scss and start there. Nesting. Write child selectors inside the parent instead of repeating it: .card { padding: 1rem; a { color: rebeccapurple; } } compiles to .card { padding: 1rem; } and .card a { color: rebeccapurple; }. The & parent reference, useful for states: .button { background: #0073aa; &:hover { background: #005177; } &.active { background: #003f66; } } compiles to .button, .button:hover and .button.active with those three colours. Variables. Set a value once and reuse it: $brand: #0073aa; a { color: $brand; } .button { background: $brand; } One thing that surprises people: the default output style is compressed, so the compiled file is one long line and rebeccapurple has become #639. Set the compiler to Expanded while you are learning. Full language guide: https://sass-lang.com/guide/

Does the plugin change files in my theme?

Yes, one kind of file. It is a compiler, so the boundary matters more than a reassuring "no". It writes the CSS target of each pair, for example assets/css/style.css. That file is replaced in full on every compile, so treat it as build output and never hand-edit it. It also creates the directory for that file when the directory does not exist. Each compile briefly writes a second file next to the target, style.css.tscsscompiler-tmp, and renames it onto the target when it is finished — that is what makes the swap atomic. It is gone by the time the compile returns. The name deliberately does not end in .css, so if a process is killed part-way the leftover is never served as a stylesheet; you can delete it. It writes nothing else. Your SCSS is only read. functions.php, templates and every other non-.css file are unreachable, because a target without a .css extension is refused before the write. Nothing outside the active theme can be written either: .. is rejected, and a target that turns out to be a symlink pointing out of the theme is rejected too. Cache-busting versions are never written into a file. They go onto the stylesheet URL at request time through the style_loader_src filter, so your theme's own wp_enqueue_style() calls stay as they are. One thing to watch: the target does not have to be a new file. Point a pair at a .css file that already exists in your theme and that file is replaced.

Can I compile into my theme's own style.css?

No, that one target is refused. style.css is where WordPress reads Theme Name: from, and a compile replaces the whole file, so one typo there would leave the theme unrecognisable and drop it out of Appearance → Themes. The plugin says so instead of writing. Compile to a separate file and enqueue it alongside your style.css: assets/scss/style.scss → assets/css/style.css Any other name works too. Only the theme's own root style.css is off limits; a style.css in a subfolder is fine.

When does the cache-busting version change?

Only when that pair's freshly compiled CSS differs from the file already on disk. Other pairs keep their version. An edit that produces identical CSS does not move the version. That covers // comments in any mode, and /* */ comments in compressed output, because those are stripped. In expanded output a /* */ comment is part of the CSS, so editing one is a real change and does bump the version. Versions are three-part numbers and the patch digit advances: 1.0.4 becomes 1.0.5.

What is the difference between Auto-compile and the auto-recompile timer?

Two separate settings, with different defaults. Auto-compile is on by default. On every wp-admin page load it checks whether any compiled CSS is missing or older than its SCSS source or tracked partials, and rebuilds what needs it. Nothing runs on a schedule; your own page load triggers it. Auto-recompile is off by default. Turn it on and the settings page runs a countdown, from 1 to 999 seconds, and re-checks on each tick while that page stays open in your browser. It counts down only while that tab is the one you are looking at, and it holds off while the form has unsaved changes; the readout says which of the two is happening. It compiles only when something actually changed, so a page left open does not keep rewriting the CSS. They are independent. Switching Auto-compile off does not stop the timer. To compile only on the button, switch both off.

My CSS is not updating. What do I check?

In this order:

  1. Did you save the settings? Compile now is locked while the form has unsaved changes, and clicking it then does nothing.
  2. Press Compile now and read the message. A missing source file or a syntax error shows up here.
  3. Look at the pair's version. If it did not move, the compiled CSS came out identical and the browser is correctly serving what it has.
  4. Check whether "Bump CSS version after each compile" is on. With it off the URL never changes and browser caches keep the old file.
  5. If pairs come from TSCSSCOMPILER_PAIRS, versions are never rewritten. Edit the version value in your code.
  6. Check your caching plugin and CDN. A version in the URL does not help if something upstream serves a cached response.

Where do the compiled CSS files go, and what if they are missing after a deploy?

Each target is written inside the active theme at the path you configured. With Auto-compile on, the next wp-admin page load notices a missing file and builds it, so a deploy or a git pull without compiled CSS does not leave the site unstyled. Nothing rebuilds until someone with access opens an admin page, though, and the pair is not enqueued in the meantime. Deploying to production without visiting wp-admin means you should commit the compiled CSS.

Does it detect changes in @import-ed partials?

Yes. After each successful compile the plugin stores every file the compiler read, including nested import chains, and Auto-compile compares each of them against the compiled CSS. So if style.scss does @import "menu"; and you only edit _menu.scss, the next admin page load recompiles. You never have to touch the entry file.

Does anything compile on the front end, for visitors?

No. Compiling happens only in wp-admin, for a logged-in user who passes the capability check, and it is triggered in exactly three places: an admin page load with Auto-compile on, the Compile now button, and the settings-page timer. The last two are admin-ajax requests, so also wp-admin. There is no WP-Cron job and no scheduled task. Visitors are served the already compiled file and never cause any Sass work.

Where do I see compilation errors?

On Tools → Theme SCSS Compiler. A failed compile is kept in a "Last compile error" panel with server paths stripped from the message, and a successful compile clears it. The stored error also expires by itself after 24 hours. Compile now reports success or failure right away. There is no notice on other admin screens, so a failed auto-compile is visible only on the plugin's own page.

Frontend or Admin: what does the Context setting do?

Each pair has one. Frontend pairs are enqueued on wp_enqueue_scripts, so on the public site only. Admin pairs are enqueued on admin_enqueue_scripts, so in wp-admin only. The version filter applies in both. New pairs start as Frontend.

Does it work with child themes?

Yes, and the active child theme is the boundary. Paths resolve against the active stylesheet directory, which is the child when one is active. That is also the limit: a path containing .. is refused, and while an @import can read a file in the parent theme, the compile is stopped before anything is written. If the SCSS you want to compile lives in the parent theme, configure it while the parent is active, or copy the sources into the child.

Can the CSS target sit outside the theme?

No. Both paths resolve against the active theme, a path containing .. is rejected, the target must end in .css, and a symlinked target pointing out of the theme is refused. Writes cannot leave the theme. Reads are a different matter. An @import in your SCSS can name a file anywhere the PHP process can read. That file is read and parsed, and only then does the plugin refuse to write output built from it. The contents never reach a public CSS file, but anyone who can edit the SCSS can make the compiler open a file elsewhere on the server. Treat access to this plugin the way you treat access to the theme editor.

What happens if I deactivate or delete the plugin?

Deactivating stops the enqueuing. If the plugin was enqueueing your stylesheets, they are gone from the page until you reactivate it or add wp_enqueue_style() to your theme. The compiled CSS files stay on disk. Deleting removes the plugin's options and transients, on every site of a multisite network. It does not delete the compiled CSS in your theme. Those files are yours and remain.

Should the plugin enqueue my CSS, or should I do it in functions.php?

Either. Auto-enqueue is on by default: the plugin calls wp_enqueue_style() for each pair on that pair's context, as late as the hook allows, after everything else has registered, and skips a file whose URL is already registered so it does not load twice. That check is a URL match at the moment the hook runs, so a stylesheet registered later, or registered under a differently written URL such as http:// against https://, can slip past it. Enqueueing in functions.php is more predictable, and it means the front end does not depend on this plugin staying active. Switch Auto-enqueue off and enqueue the compiled file yourself. The version filter keeps working either way.

Who can use the plugin, and what does that permission allow?

Administrators only, through the manage_options capability. The menu entry, the save handler, both AJAX endpoints and the auto-compile hook all check it, so Editors and Authors neither see nor reach it. To open it up, filter the capability: add_filter( 'tscsscompiler_capability', static function () { return 'edit_theme_options'; } ); Do that carefully. Whoever passes the check can set the file pairs, write CSS into the active theme, publish it on the front end, and make the compiler read any file the web server can read. It is a code-adjacent permission, so keep it with roles you already trust with the theme.

Can I configure everything in code instead of the admin form?

Yes, one setting at a time. Define the constant for the setting you care about and it wins over whatever the database holds. You do not have to move the rest of your configuration into code with it. // Enough on its own. The database keeps every other setting. define( 'TSCSSCOMPILER_OUTPUT_STYLE', 'expanded' ); A setting a constant supplies is shown on the settings page but its control is disabled, and the page names the constants in play so you know where to look. That is deliberate: an editable control would let you store a value that the constant overrides on the very next page load. Here is the full set. Where a constant is absent, the value after the arrow is the fallback the database starts from.

  • TSCSSCOMPILER_AUTO_COMPILE, true / false → true
  • TSCSSCOMPILER_AUTO_ENQUEUE, true / false → true
  • TSCSSCOMPILER_OUTPUT_STYLE, 'compressed' / 'expanded' → 'compressed'
  • TSCSSCOMPILER_AUTO_RECOMPILE, true / false → false
  • TSCSSCOMPILER_AUTO_COMPILE_INTERVAL, seconds from 1 to 999 → 30
  • TSCSSCOMPILER_BUMP_VERSION, true / false → true
The file pairs work the same way, with one extra consequence: define( 'TSCSSCOMPILER_PAIRS', [ [ 'scss_path' => 'assets/scss/style.scss', 'css_path' => 'assets/css/style.css', 'version' => '1.0.0', 'context' => 'frontend' ], [ 'scss_path' => 'assets/scss/style-admin.scss', 'css_path' => 'assets/css/style-admin.css', 'version' => '1.0.0', 'context' => 'admin' ], ] ); All four keys are expected: scss_path, css_path, version (three-part, or it falls back to 1.0.0) and context (frontend or admin, anything else becomes frontend). The consequence: with the pairs in code the version numbers are in code too, so nothing can bump them. Version bumping is switched off and locked while TSCSSCOMPILER_PAIRS is defined, whatever TSCSSCOMPILER_BUMP_VERSION says. Edit the version value yourself when you want the URL to change. Whatever the constants override stays untouched in the database, so removing a define() again gives you back the value you had before.

Where do I put the define() calls?

wp-config.php is the simplest place, and it loads before any plugin: define( 'TSCSSCOMPILER_PAIRS', [ [ 'scss_path' => 'assets/scss/style.scss', 'css_path' => 'assets/css/style.css', 'version' => '1.0.0', 'context' => 'frontend' ], ] ); define( 'TSCSSCOMPILER_OUTPUT_STYLE', 'compressed' ); Your theme's functions.php works as well. Hook after_setup_theme; the plugin does not read its settings before then: add_action( 'after_setup_theme', static function () { if ( ! defined( 'TSCSSCOMPILER_PAIRS' ) ) { define( 'TSCSSCOMPILER_PAIRS', [ [ 'scss_path' => 'assets/scss/style.scss', 'css_path' => 'assets/css/style.css', 'version' => '1.0.0', 'context' => 'frontend' ], ] ); } } ); Bedrock and .env. The scalar options bridge straight through, which is the usual reason to reach for this: expanded output on staging, compressed in production, one line per environment. .env TSCSSCOMPILER_OUTPUT_STYLE=expanded config/application.php if ( $style = env( 'TSCSSCOMPILER_OUTPUT_STYLE' ) ) { define( 'TSCSSCOMPILER_OUTPUT_STYLE', $style ); } Pairs are a nested array and do not fit in .env. If you want those from code too, put the define( 'TSCSSCOMPILER_PAIRS', [ … ] ) in config/application.php directly.

Why is one of the settings greyed out?

Because a TSCSSCOMPILER_* constant supplies it, from wp-config.php, your theme, or a Bedrock bridge. Only that setting locks; everything you did not define a constant for stays editable and saves normally. The notice at the top of the page lists the constants it found, so you know what to go looking for. Saving is refused on the server for a locked setting, not merely disabled in the browser, and the value stored in the database is left as it was. Define TSCSSCOMPILER_PAIRS and the file-pair list locks the same way.

Does it work on multisite?

Yes, per site. Settings and the tracked partials are per-site options and the page is a per-site Tools submenu, so a network-activated plugin has to be configured on each site separately. There is no network admin screen. Deleting the plugin cleans up on every site of the network.

What does the Theme CSS status table show me?

Every .css file it finds in the active theme, annotated with what is known about it: whether it belongs to a compiler pair, with that pair's version and context; whether it happens to be registered in the current request; or whether it is simply a stylesheet that lives in the theme. The theme's own style.css shows the version from its theme header; the other files have none to show. It answers "what else is loading CSS in this theme". It scans the active theme only, so with a child theme active the parent's stylesheets do not appear. It skips vendor, node_modules, bower_components and hidden folders, and stops at 250 files with a notice.

Is the admin UI accessible?

The settings page is built and measured against WCAG 2.1 AA. Every field has a label tied to it, including the rows you add with the Add file pair button. Each row is a named group ("File pair 2"), so a screen reader tells you which pair a field belongs to instead of reading four identically-named fields over and over, and the numbering is rewritten when you add or remove a row. Removing a pair moves focus to a neighbouring control rather than dropping it on the page body. Compile feedback goes through role="status" and role="alert" regions and is announced once, politely. The countdown itself is deliberately not a live region, so it does not read out every second, and it has a Pause auto-recompile button so you can stop it. If a file pair is rejected on save, the page says so in text and explains why, rather than reporting a plain "Settings saved." Contrast is measured, not estimated: text is at 4.5:1 or better and every control boundary and focus indicator at 3:1 or better. Focus is drawn with a real outline instead of a shadow, so it survives Windows High Contrast mode, where the custom checkbox and selects hand rendering back to the browser. Decorative icons are aria-hidden, headings run without skipping a level, and the status table scrolls inside its own box so the page never scrolls sideways. Not yet done, for honesty: input that was rejected on save is not handed back to you for correction, so WCAG 2.2's Redundant Entry is still open. If you hit an accessibility problem, please report it.

Is it translated into German?

Yes, German ships with the plugin and the catalogue is complete: every admin string, hint and error message. It loads on every supported WordPress version. Worth knowing if you maintain something similar: WordPress only started registering a plugin's own languages/ directory by itself in 6.8. On 6.3 through 6.7 the plugin registers that directory with WordPress as it loads, and the catalogue is then read on demand at the first translated string. A wordpress.org language pack still takes precedence.

更新日志:

1.0.8 1.0.7 1.0.6 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1 1.0.0