Page MenuHomePhabricator

Side-by-side comparisons of live and sandbox versions of a template fail because both live and sandbox stylesheets use the same selectors
Closed, ResolvedPublic

Description

Some protected, complex, or well-tested templates have a sandbox version for testing before making changes that will affect live articles. With the introduction of TemplateStyles, that also means live and sandbox versions of the stylesheet.

Some of these templates also have "test case" pages that display side-by-side comparisons of the output of the live and sandbox versions of the template so rendering changes can be more easily observed. But since the live and sandbox versions use the same selectors, this fails to accurately show the differences since both wind up being affected by both stylesheets.


Original report

This one came up in conversation at en.WP at WT:TemplateStyles a bit of the way down the section.

There may not be a good technical solution to this, but I figured I'd get it on the workboard and see if there is one at all. :)

(Keep in mind MCR I guess?)

Event Timeline

Edit Template:Thermometer/sandbox1/styles.css, put Template:Thermometer/sandbox1 into the "Preview page with this template" form field and press Preview? Works fine for me.

Yes, that's one way to work around the issue but not how we use test case pages generally, which can be lengthier (ref Template:Infobox/testcases), especially since test case pages are often used to demonstrate to a non-technical editor how the template would look after the sandbox is implemented.

I still don't understand what the issue is. How else would you test an unsaved change to a template?

The one complaint I actually see there is that the documentation transcluded into the sandbox won't display the right box from {{Uses TemplateStyles}}. There's nothing to do here about that, you'll have to do something onwiki about it. Maybe pass {{FULLPAGENAME}}/styles.css to your template?

What you seem to be getting at here is different and doesn't seem to be directly mentioned there at all: you can't have a side-by-side comparison of the live and sandbox versions of a template on one page, because the styles of whichever is transcluded second will override the other. There's not any really good workaround for this.

  • The simplest would be to change your sandbox template and stylesheet to use different selectors
  • Or you could duplicate all the selectors, like .foo, .side-by-side-live .foo { ... } in the live version and .foo, .side-by-side-sandbox .foo { ... } in the sandbox version, and then have divs with those classes wrapping the transclusions on the side-by-side page.
  • We could potentially add a parameter to <templatestyles/> to specify an additional class to prefix all selectors for this testing, so <templatestyles src="Foo/styles.css" testprefix="sandbox"/> would turn .foo { ... } into .mw-parser-output .sandbox .foo { ... } (versus just .mw-parser-output .foo { ... } without the "testprefix" parameter).

Edit Template:Thermometer/sandbox1/styles.css, put Template:Thermometer/sandbox1 into the "Preview page with this template" form field and press Preview? Works fine for me.

That only really works if what you want to test is visible on Template:Thermometer/sandbox1. Other ways to use TemplateSandbox include:

  • Make your changes in the edit window of the live stylesheet, then use "Preview page with this template" to see how the changes affect any other page.
  • Edit the sandbox stylesheet. Then in the edit window of the live template alter the src of the <templatestyles/> (plus any other changes you want to make) and use "Preview page with this template" to see how the changes affect any other page.
  • Create a sandbox template and stylesheet under the same prefix, e.g. User:Example/sandbox/Template:Foo and User:Example/sandbox/Template:Foo/styles.css. Then use Special:TemplateSandbox.

What you seem to be getting at here is different and doesn't seem to be directly mentioned there at all: you can't have a side-by-side comparison of the live and sandbox versions of a template on one page, because the styles of whichever is transcluded second will override the other.

@Evad37 comments at 01:13, 26 July 2018 (UTC) followed by @TheDJ "oh, yeah, we didn't think about that", but yes, that's the one.

  • The simplest would be to change your sandbox template and stylesheet to use different selectors

This is simple but emphatically not expedient for complex templates (nor, even, not-complex templates, such as the sandbox I made at Template:Talk quote inline/sandbox). Part of the sandbox workflow is "I copy template sandbox to template, done", which this adds an intermediate step that must be remembered.

  • Or you could duplicate all the selectors, like .foo, .side-by-side-live .foo { ... } in the live version and .foo, .side-by-side-sandbox .foo { ... } in the sandbox version, and then have divs with those classes wrapping the transclusions on the side-by-side page.

If a class isn't called in the (expanded) template text on a page which transcludes a particular template, does the CSS associated with that class get loaded? I assume that it does, and so you've basically doubled the stylesheet payload (which, maybe it would be fine since compression would probably iron the majority of that back out--you still have a double-size file living on the host's computer--just some thoughts).

  • We could potentially add a parameter to <templatestyles/> to specify an additional class to prefix all selectors for this testing, so <templatestyles src="Foo/styles.css" testprefix="sandbox"/> would turn .foo { ... } into .mw-parser-output .sandbox .foo { ... } (versus just .mw-parser-output .foo { ... } without the "testprefix" parameter).

Does that solution work given response to first bullet? I think it does, but I'm new to the extension as is most of en.WP. :)

  • Or you could duplicate all the selectors, like .foo, .side-by-side-live .foo { ... } in the live version and .foo, .side-by-side-sandbox .foo { ... } in the sandbox version, and then have divs with those classes wrapping the transclusions on the side-by-side page.

If a class isn't called in the (expanded) template text on a page which transcludes a particular template, does the CSS associated with that class get loaded? I assume that it does, and so you've basically doubled the stylesheet payload (which, maybe it would be fine since compression would probably iron the majority of that back out--you still have a double-size file living on the host's computer--just some thoughts).

It doesn't double the stylesheet payload. It does increase it somewhat though, and increases the maintenance burden.

.foo, .bar,
.sandbox .foo, .sandbox .bar  /* ← this is the only line added. Each rule needs a similar line. */
{
    color: red;
    background-color: green;
    /* and everything down else here doesn't get duplicated */
}
  • We could potentially add a parameter to <templatestyles/> to specify an additional class to prefix all selectors for this testing, so <templatestyles src="Foo/styles.css" testprefix="sandbox"/> would turn .foo { ... } into .mw-parser-output .sandbox .foo { ... } (versus just .mw-parser-output .foo { ... } without the "testprefix" parameter).

Does that solution work given response to first bullet? I think it does, but I'm new to the extension as is most of en.WP. :)

You'd have to change "copy the template, done" to "copy the template, edit the <templatestyles/> tag, done". But you probably already have to do that to point to Template:Foo/sandbox/styles.css in the first place.

Then on your side-by-side comparison page, you'd have to enclose the sandbox's transclusion in a <div class="sandbox"> (or whatever class matches the 'testprefix' used). Although for this to really work well the 'testprefix' would need to duplicate the selectors rather than just use a different prefix so the sandbox could be tested in other contexts without the wrapper div.

It doesn't double the stylesheet payload. It does increase it somewhat though, and increases the maintenance burden.

.foo, .bar,
.sandbox .foo, .sandbox .bar  /* ← this is the only line added. Each rule needs a similar line. */
{
    color: red;
    background-color: green;
    /* and everything down else here doesn't get duplicated */
}

Ah yes, brain fart. Probably a negligible increase in the payload after compression. The maintenance piece was more pressing to me anyway, I would say.

You'd have to change "copy the template, done" to "copy the template, edit the <templatestyles/> tag, done". But you probably already have to do that to point to Template:Foo/sandbox/styles.css in the first place.

Then on your side-by-side comparison page, you'd have to enclose the sandbox's transclusion in a <div class="sandbox"> (or whatever class matches the 'testprefix' used). Although for this to really work well the 'testprefix' would need to duplicate the selectors rather than just use a different prefix so the sandbox could be tested in other contexts without the wrapper div.

Indeed to the former paragraph--that is one thing that does need to change anyway, so that is minor.

What do you mean by the work really well note?

I think this solution would be sufficient here for me, but maybe toss this around and see if anyone falls my way.

Adding a prefix in the tag, which duplicates the selectors, seems like the best way forward to me. That way you can have

<templatestyles src="Foo/styles.css" testprefix="live"/>

in the live template, and

<templatestyles src="Foo/styles.css" testprefix="sandbox"/>

in the template sandbox. These would expand a selector like .foo into

.foo, .live .foo

for the live template, and

.foo, .sandbox .foo

for the sandbox template. On the testcase page, templates like {{testcase table}} and {{testcase rows}} can automatically apply the relevant class to a div or other element around the template invocations (or it could be done manually), and because the selectors are duplicated rather than just prefixed either the live or sandbox template can be used/tested (by themselves) on other pages. Perhaps even have a default prefix, so that testprefix doesn't need to be specified in live templates?

What do you mean by the work really well note?

To support testing the sandbox version by opening a page in the editor, swapping the invocation of the live version for the sandbox version, and hitting "preview", it needs to generate selectors without the .sandbox inclusion too.

Perhaps even have a default prefix, so that testprefix doesn't need to be specified in live templates?

I'd rather not, since the use of side-by-side comparisons doesn't seem so widespread that every stylesheet in every live article should have the overhead.

Anomie renamed this task from Test case pages: new styles cannot be tested (easily) because the sandbox styles page uses the same selectors as the main template page to Side-by-side comparisons of live and sandbox versions of a template fail because both live and sandbox stylesheets use the same selectors.Jul 27 2018, 1:37 PM
Anomie updated the task description. (Show Details)

I note the "testprefix" solution would still have rendering issues in certain cases.

Where the difference between the live and sandbox versions is changing the specificity of a selector, the non-testprefixed version from one could override the testprefixed version of the other. For example, if you change between #foo and .foo, the side with .foo will be overridden because .mw-parser-output #foo has higher specificity than .mw-parser-output .sandbox .foo.

Where a style is added or removed, the non-testprefixed version from the other side will still apply that style. For example, if you change between .foo { color: red; } and .foo { color: red; background: white; }, both sides will get the white background color. Or if you add or delete .foo { color: red; } entirely, both sides will get the red coloring.

How about having a testpage= parameter in the tag, in addition to testprefix=, so that on the specified test page the prefix is applied (without duplication), but on other pages the prefix is not applied. Perhaps also subpages of the specified page could have the prefix applied, so you could have multiple testcase pages.

  • We could potentially add a parameter to <templatestyles/> to specify an additional class to prefix all selectors for this testing, so <templatestyles src="Foo/styles.css" testprefix="sandbox"/> would turn .foo { ... } into .mw-parser-output .sandbox .foo { ... } (versus just .mw-parser-output .foo { ... } without the "testprefix" parameter).

Since the test and the real one need both be prefixed to avoid bleedover, maybe there should be something like <templatestyles src="Foo/styles.css" wraper=".ts-foo"/> with the expectation that wrapper is where you put the class/ID of the wrapping element (something that all TemplateStyles-enabled templates need anyway) and the sandbox version would just use a different wrapper.
(Doesn't work so well with T197617: TemplateStyles should be able to add skin-specific CSS though. The skin class could be put inside the wrapper parameter but that's conceptually wrong.)

Change 468053 had a related patch set uploaded (by Anomie; owner: Anomie):
[mediawiki/extensions/TemplateStyles@master] Add "wrapper" attribute to <templatestyles/>

https://gerrit.wikimedia.org/r/468053

Change 468053 merged by jenkins-bot:
[mediawiki/extensions/TemplateStyles@master] Add "wrapper" attribute to <templatestyles/>

https://gerrit.wikimedia.org/r/468053

Tgr assigned this task to Anomie.

User-notice: the <templatestyles> tag has a new wrapper parameter now, which produces selectors like .mw-parser-output <wrapper parameter value> <selector from CSS page>.

Trappist_the_monk subscribed.

Functionality that "exists" but isn't at all documented, doesn't exist. Please, those of you who know how this wrapper thing works, document it so that the rest of us can learn to use it correctly.

Functionality that "exists" but isn't at all documented, doesn't exist.

Sure it does. It just needs improved documentation.

I added some documentation for you in Special:Diff/3401464. Feel free to build upon it.