Following on from Adding Unique Class Names


#1

Echoing much of the sentiment below, it would be very useful if user editable classes could be added to principle client side elements - categories being a prime example.

I appreciate that almost every element can be targeted by CSS as it stands, but selector authoring and testing can be time-consuming and there’s no guarantee that complex selectors will work following core upgrades. There is also no facility to target unassociated elements with a single CSS class.

I am not advocating classes for everything, just the major building blocks which would supplement the excellent custom CSS/HTML/JS features available in Admin. And if these CSS/HTML/JS includes could be rendered only to certain TLs, that would be even awesomer < new word :slightly_smiling:

Continuing the discussion from Adding unique class names to the elements in the topic map:


(Jeff Atwood) #2

Not really following. Can you provide specific examples from the HTML view source with your proposed changes?


#3

This is not exhaustive as I don’t want to spend too much time on it unless you see some mileage in adopting it. I can draw up something in more detail if needed.

But for starters:

###Allow user to add category classes in the category edit popup, like this:

###Dom location 1 - List of Categories

The new class names would be added to the TR element shown below when a list of categories are being displayed. I say TR as opposed to the child TD which is classed as category, because that will enable the row to be selected directly for hiding and so on. As you can see, selecting/targeting the BMW category in dev tools selects the entire category on the page:

###Dom location 2 - Category and Sub-Categories

Similar to the above, although this time viewing a category and sub category.

The parent category class could be added to the body tag alongside the existing category class - see 1. Having the class there follows the existing convention and allows the entire page to be targeted as well as the individual sections themselves. As you can see, I use banners extensively. So for example, being able to assign one class to several categories allows me to assign a banner to this class and control, with ease, where it appears.

As per the parent category example above, the child category classes could be added to the TRs shown in 2. Having classes here as well as the parent class in the body allows me to target everything as a group (body.class) , or differently for each sub category depending on the use case.



###Custom CSS - JS - HTML - restrict by Trust Level

I mentioned the following too. This is part of a bigger requirement which should probably be part of a separate topic. I will put something together when requirements on that have firmed up, or sooner if needed.


Keyboard shortcut scrolling does not consider fixed header
(Mittineague) #4

In my limited experience with styling Discourse, I have found that by using a combination of id, class and relationship selectors I have been able to target what I wanted to style with success.

Always easy? No. Convenient? No. But possible? Yes.

Personally. I cringe when I see mark-up that has class values added to page elements in abundance (cough - WordPress - cough)
But, it is convenient for those that may have trouble determining relationship selectors.

This is not to say that Discourse doesn’t have any elements that can’t be specifically targeted. Just that I have not found any yet.
Nor is it to say that adding ids or class values to the elements that others more commonly want to target couldn’t be beneficial.

As for Trust Level CSS, AFAIK the HTML sent to the browser does vary depending on whether or not the visitor has JavaScript enabled and whether or not they are logged in. But not by their Trust Level.
For that some JavaScript magic would be needed.


#5

Yes - although the ability to add a class to unrelated elements and then select them by a single selector is key here.

I seem to recall there’s a few orphans which rely on nth selectors and things like that which is obviously not a good thing when the DOM is provided by SAAS.

I’d also like to see the ability to add classes to posts as currently I have to add a tag to select a post, unless I use the ID - which is cumbersome. So instead I use a jQuery selector to find posts with a target tag that also have images in a Lightbox - this then allows me to insert a gallery control at run time. For example:

'#main-outlet:has(#topic-title a[class*="tag-carousel"]) div[data-post-number="1"] article .topic-body div.cooked:has(.lightbox-wrapper):not(.carousel-enabled)';

Discourse is great and in my view has so much potential on the front end - it’s almost limitless.

Edit: Yes, that’s what I gathered by looking at the DOM. I am hoping it’s not too much work on the back end to add another check when the permissions are applied. Btw, this is not a major requirement (which I should have said first time round). It’s just another tool in CSOM toolkit.

PS, thanks for your help.


(Simon Cossar) #6

Adding category class names to the table rows in the category-list seems like a good idea - the topic-list has them.

Discourse could whitelist a class for divs and spans that was something like custom-class-(class-number) with class-number being any digit. The classes generated from that (custom-class-1, custom-class-2, etc.) could then be defined with custom css, or used to target a post with javascript.

To do that, maybe you could define a css rule that works on multiple selectors:

.category-this,
.category-that {
  display: none;
}

(Kane York) #7

If you’re doing selectors on the category list, you can just use this:

.categories-list .categories {
  tr[data-category-id="34"], tr[data-category-id="32"] {
    // styles...
  }
  tr[data-category-id="61"] {
    // styles...
  }
}

(Kane York) #8

Shouldn’t you be using the decoratePost() hook for this? That lets you get code run once per post display.


#9

Thanks but that means the CSS has to be updated every time relevant changes are made to categories - hence why, in my view, it’s better to write class level selectors, and assign the classes within the categories.

Edit:

Thanks for the suggestion, I’m not familiar with Discourse in depth yet. I am sure I will be able to refactor most of the jQuery stuff once I am, but for now it works very.


(Kane York) #10

Huh? You were going to have to update something, in any case. Unless you’re swapping the category names, they keep their IDs.


#11

How come? See my screen grabs above. If classes can be added to categories then class selectors will work fine and won’t break IF the DOM changes over time.


#12

Thanks Simon. Still very new to how Discourse handles things. Would whitelisting be done via plugin or in core?


(Dean Taylor) #13

For the category list I would recommend using ID’s they don’t change over time and are fixed at the point of creation.

I manage several Discourse instances with 50+ categories for 2 of the instances and when it get’s to this high a number ID’s are the way to go.

Now sometimes you need visibility to those ID’s as a developer for a short while to setup the CSS and other customisations - if for you using Dev Tools is an issue then you can expose the ID’s via CSS and HTML Customizations added to </body> like this one:

<script>
if (window.jQuery) {
    window.jQuery(function ($) {
        var PageTracker = require('discourse/lib/page-tracker').default,
            updateCategory = function () {
                $('.categories .category h3').each(function( index ) {
                    var h = $( this );
                    h.append( ' - ' + h.parent().closest('tr').data('category_id') );
                });  
            };
        PageTracker.current().on('change', function (url) {
            setTimeout(function () {
                updateCategory();
            }, 100);
        });

    });
}
</script>

You can see a screen grab of the kind of output here:

After you are done with your CSS customisations I would recommend then turning this customisation off.


(Simon Cossar) #14

It can be done either way. Here’s an example of doing it in a plugin.

The file that adds to the whitelist is this one discourse-grid/whitelist-tags.js at master · scossar/discourse-grid · GitHub


#15

Thanks for replying.

Everybody has their pet way of doing things. :slightly_smiling: My preference is to create a small group of classes and assign those on demand as in my first screen grab above. Given Discourse provides a simple way of creating the class descriptions in Admin, for me that would be the best way to go - not least because it allows anybody with category edit permissions to fundamentally alter how that category renders, where it renders (flex etc) and when it renders. Multiply these class combinations together as in a | b | c and you have a very powerful sub system. Of course, you can do this your way, except that it won’t be possible to change things, unless I’ve misunderstood your suggestions, without editing the selectors every time you need to change something.

Thank you, that code is very useful. A question for you given you manage live environments. Do you add external scripts? And if so, do you use require?


#16

Thank you. Very useful, although I can’t see me venturing into the plugin route.

Perhaps though, Discourse will allow tag whitelisting in Admin, which in turn could be used in topics for certain TLs, or perhaps Mods or Admins. That would seem like a good solution to me, although I am saying that with little to no experience of whether that would be technically feasible, or aligned with core Discourse philosophy.


(Dean Taylor) #17

Discourse allows use of SCSS within the custom CSS section.

You might want to review SCSS especially @extend / inheritance.

You can find yourself with very DRY SCSS code even when using ID’s.


#18

Thanks, we use SCSS across a number of projects, but didn’t realise we could use it here.


(Dean Taylor) #19

Yeah - you can also import some of the helpers e.g. .clearfix:

@import "common/foundation/helpers.scss";

Or get your hands on the colours you’ve defined in the Customize > Colors section:

@import "common/foundation/variables";
img.avatar {
  border-color: $primary;
}

You can see more examples of variables usage in the source code:


#20

Ahhh… bit by bit it unravels… thank you.