Ceci est une ancienne révision du document !
This month is the last part of our exploration of the new Selectors and CSS dialog (“Selectors dialog” for short). As with the previous parts, we’ll be working with this collection of shapes arranged as four groups of objects, one for each row. We’ve previously looked at using class and element selectors in the dialog, but, as you may recall from part 112, CSS allows for a variety of other selectors, and different ways to combine them. Inkscape’s CSS parser doesn’t support all the various possibilities, but it does manage some of them. Whether or not they’re actually useful to you is another question entirely, but, in this instalment, I’m going to take a look at some advanced selectors that do work, as well as some that unfortunately don’t. Starting with a blank Selectors dialog, I’ve created a class called “squares” that includes all the squares in our sample image, and a second class called “col-1” which contains all the elements in the first column of objects. As you can see, the “#rect31” element appears in both, as you would expect.
Deselecting everything in the drawing, and clicking the plus button at the bottom of the dialog, triggers the dialog for adding a new selector – pre-filled with “.Class1” as usual. As we saw last time, it’s also possible to enter an element name here, but, this time, we’ll create something even more complex: a selector that targets multiple classes. If we enter a value of “.squares.col-1” as a single string, with no spaces, the selector will target only those elements that have both the “squares” and “col-1” classes applied. In our case the only thing that matches that combination is the square at the top left. Alternatively, we can combine an element selector with a class selector. Entering “rect.col-1” for example, will only match those <rect> elements that also have a class of “col-1” applied. Again, the only thing that matches in this case is the square at the top left. Using these selectors, I’ve added rules to change the color of any object where both classes match, and the stroke of any <rect> with the “col-1” class. The effect is that the square at the top left has both the new rules applied, but no other elements are affected.
The ability to combine classes in this way could potentially be useful, especially if you want to use class names to categorize your elements. When producing game assets, for example, you might have multiple images in a single document, covering different types of landscape in different seasons, and with different assets. Need to quickly find the image for a wintery forest tile with a mine? Add a new selector for “.winter.forest.mine” – assuming you’ve already set the right classes on your images. Combining elements and classes is probably less useful, particularly given how many of Inkscape’s primitives are actually just <path> elements in the underlying SVG, and are therefore indistinguishable from each other via a simple element selector. If you can think of a good use case for this, however, it’s nice to know that Inkscape already supports the format. The fact that Inkscape piles its own internal attributes onto <path> elements in order to support some of its basic shapes does lead onto another type of CSS selector that’s worth further examination: the attribute selector. In the world of CSS it’s possible to select elements that have a particular attribute, or which have an attribute set to a particular value. The table below covers the main selectors that will work in a web browser.
There are other variations to find substrings only at the start or end of the value, or which force case sensitivity when matching, for example. If you’re a web developer who wants to know more about attribute selectors, I recommend looking them up on the Mozilla developer site (link at the end of the article). For our purposes, these should allow us to distinguish between different types of primitive in Inkscape. Let’s look at one of our star shapes in the XML editor (shown bottom right). That’s a long list of attributes, but the one we’re most interested in is the last one: “sodipodi:type” with a value of “star”. As I’ve discussed previously in this column, the “sodipodi” part is the namespace for this attribute, which is required because it’s not part of the SVG specification. In practical terms, however, I usually just refer to this as the “type” attribute. Suppose we want to target just the stars with a CSS rule. Based on the table of attribute selectors you might expect [type=“star”] to do the job, but it doesn’t. Not even a more basic selector of just [type] is accepted by Inkscape.
Perhaps it’s that pesky namespace. How about [sodipodi:type] or [sodipodi:type=“star”]? No, they don’t work either. In fact namespaces in CSS are a bit of a pain, requiring you to redefine your prefixes in the CSS in addition to the definition in the XML itself. In the selector rule, the namespace is then separated from the value by a pipe character, not a colon. So, in theory, manually adding an “@namespace” rule to the stylesheet, then using [sodipodi|type=“star”] should do the job. But not in Inkscape. I’ve tried many, many different combinations of attribute selector, both with and without namespaces, but can’t get any of them to work within Inkscape. This is a real shame as it makes it impossible to target specific Inkscape primitives, or elements with other proprietary attributes. If you’re a web developer, then you may wish to know that these selectors do work as advertised in web browsers, provided you include the CSS namespace declaration and use a pipe separator. This <style> block, for example, will cause all the stars in the test document to appear with an orange fill within a web browser, but it doesn’t work when the same file is loaded into Inkscape.
<style id=“style258”> @namespace sodipodi url(http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd); [sodipodi|type=“star”] { fill: orange !important; } </style> Although Inkscape doesn’t work with attribute selectors, there are a few other useful CSS rules that it does seem happy with. First, we have the descendent selector: simply enter two selectors with a space between them, and the rule will match only if the element matching the second selector is some descendent (in the XML structure) of an element matching the first selector. For example, in our test file, each row of objects is in a separate group, and I’ve set the ID for each group (using the Object Properties dialog) to “row-1”, “row-2” and so on. If I want to select all the <path> elements in row 3 (i.e. the star and Bézier path objects), then I can create a selector with the string “#row-3 path” to find every <path> element, but only if it’s a descendent of the element with the ID “row-3”.
Note that “descendent” in this case means any child, grandchild, great-grandchild and so on down the tree. If you want to select only immediate children, then use a “>” character between the individual parts of the selector (with optional white space around it for readability). Sometimes you might want a single set of CSS rules to apply to several different selectors. In that case, you can comma-separate the individual items. So, to create a rule that matches three specific elements by their IDs, use something like “#rect31, #rect1247, #path729”. The easiest way to achieve that is just to select them all before you press the “+” button at the bottom of the dialog: the field that opens for you to enter a selector will be pre-populated with a comma-separated list of the IDs. You can use commas to mix any types of selector, so creating a rule to match any immediate child of the second row plus any circle, would result in this: “#row-2 > *, circle”. Notice from this example that you can also use an asterisk (“*”) to match any element, regardless of its type or class. Comma-separated lists of selectors can easily become long and unwieldy when you need to match lots of different things. CSS has a couple of features in the form of the “:is()” and “:where()” pseudo-classes that can simplify many such rules. Although Inkscape does let you enter them into the dialog, unfortunately they don’t actually work in the program.
Also in the list of useful CSS rules that don’t work is the “:not()” pseudo-class. This should allow you to select elements that don’t match a particular rule. E.g. “:not(path)” for selecting all the non-path elements. Instead, Inkscape just swallows the new rule when you enter it – without it then appearing in the dialog at all. Prefixing it with a class selector (e.g. “.row-1:not(path)”) allows it to appear, but it certainly doesn’t work as it should. There are some pseudo-classes that do sort-of work with Inkscape, but not well enough to be genuinely useful. The “:first-child”, “:last-child” and “:nth-child()” selectors work, but only if they’re applied to a class or ID selector. For example, “.squares:first-child” will select any element with the “squares” class that is the first child of its parent. In the example file that will match the square in the top left, as it is the first child of the group element that holds the row. In theory you should be able to use just “:first-child” or “*:first-child” to match the first element of any parent but, in practice, that doesn’t work at all. This is a real shame as it makes it practically impossible to use the powerful “:nth-child()” pseudo-class to select all the odd children of a group, or every fourth one, for example. A related set of selectors are “:first-of-type”, “:nth-of-type”, plus some other “-of-type” strings. Trying to use these will actually cause Inkscape to crash entirely, so definitely steer clear of them!
To summarise my findings, the Selectors dialog is effective when used with simple class, ID or element selectors, including combining them in a comma-separated list. But most of the more powerful CSS rules either don’t work at all, don’t work as expected, or might even kill the program. It’s probably best to see this dialog for what it is: a replacement for the Selection Sets dialog that also lets you set some CSS rules in a stylesheet, should you wish to. If you’re enough of a developer to specifically need a stylesheet in your document, then you’re probably better off managing it outside of Inkscape for now. If you want to manage and store just some simple selections, on the other hand, then this dialog should serve you well enough. Over time, it’s likely that Inkscape’s CSS capabilities will improve, and perhaps some of the more complex rules will be supported. But, for now, you’re best to either keep it simple, or to do it by hand.
Links Overview of CSS resources and tutorials on MDN: https://developer.mozilla.org/en-US/docs/Web/CSS Attribute selectors on MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors The @namespace rule on MDN (works in browsers, not in Inkscape): https://developer.mozilla.org/en-US/docs/Web/CSS/@namespace