Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: New CSS classes for "themeability" #1519

Merged
merged 34 commits into from
Mar 29, 2023

Conversation

esm7
Copy link
Contributor

@esm7 esm7 commented Jan 11, 2023

Description

This PR introduces a vastly improved set of CSS classes and a restructure of how the plugin renders tasks, in both queries and Reading View, to allow a very extensive range of customization options to its look ("themeability").

The best way to understand the changes is the new "styling.md" documentation, which covers in depth the updated structure of rendered tasks and the possibilities to customize their style.

While the changes are relatively fundamental, they are also backwards-compatible, in the sense that in principle without any custom CSS this PR should not change anything about how tasks are rendered.
That being said, because of the changes to internal divs and spans, there could still be edge cases on which the Electron renderer might choose to display tasks slightly different than before, e.g. when it comes to decisions on where to word-wrap lines. If any such differences are found, I believe they will be to the favor of the new implementation, because now the renderer knows that things like a due date emoticon and its corresponding date are one unit rather than arbitrary two words.

Motivation and Context

As described in this discussion, although Tasks is a very powerful tool, when it comes to looks it is severely lagging behind almost any competing tool. And while it's possible to configure some of its aspects with CSS, I quickly discovered that the way it is structured makes this very limited. Thus comes this PR, which makes extensive changes to how tasks are rendered behind the scenes, while also maintaining backwards compatibility for those who just like the way it works right now.

How has this been tested?

  1. Extensive tests that are supposed to cover all the changed functionality.
  2. Manual testing throughout weeks of daily usage.

Screenshots (if appropriate)

2023-01-11_13-48

Types of changes

Changes visible to users:

  • Bug fix (prefix: fix - non-breaking change which fixes an issue)
  • New feature (prefix: feat - non-breaking change which adds functionality)
  • Breaking change (prefix: feat!! or fix!! - fix or feature that would cause existing functionality to not work as expected)
  • Documentation (prefix: docs - improvements to any documentation content)
  • Sample vault (prefix: vault - improvements to the Tasks-Demo sample vault)

Internal changes:

  • Refactor (prefix: refactor - non-breaking change which only improves the design or structure of existing code, and making no changes to its external behaviour)
  • Tests (prefix: test - additions and improvements to unit tests and the smoke tests)
  • Infrastructure (prefix: chore - examples include GitHub Actions, issue templates)

Checklist

Terms

@esm7
Copy link
Contributor Author

esm7 commented Jan 11, 2023

Only now after creating this PR I noticed that there is actually already an existing styles.md file 🤦
I will merge mine with the existing document.

@claremacrae
Copy link
Collaborator

Brilliant. Thanks very much for this. I've had an initial look and the docs look amazing.

One thing I wondered when reading the docs - I had the impression from some talk or doc on porting to the new Obsidian theming architecture was that it was a good idea to user variable names for colours. For example, so that the colours work well automatically if switching between Light and Dark modes.

This is why I used a variable in this sample CSS:
https://obsidian-tasks-group.github.io/obsidian-tasks/queries/explaining-queries/#styling-explain-results

I wonder if it would be worth doing the same in your new docs? Maybe consider it?
I think it may also play nicely if users or themes have customised the colour values a bit???

@claremacrae
Copy link
Collaborator

Only now after creating this PR I noticed that there is actually already an existing styles.md file 🤦
I will merge mine with the existing document.

Thanks. I think Advanced is probably a good place for these docs.

It may also have the advantage of removing the 2 conflicts, which would allow CI to run...

@esm7
Copy link
Contributor Author

esm7 commented Jan 11, 2023

Brilliant. Thanks very much for this. I've had an initial look and the docs look amazing.

One thing I wondered when reading the docs - I had the impression from some talk or doc on porting to the new Obsidian theming architecture was that it was a good idea to user variable names for colours. For example, so that the colours work well automatically if switching between Light and Dark modes.

This is why I used a variable in this sample CSS: https://obsidian-tasks-group.github.io/obsidian-tasks/queries/explaining-queries/#styling-explain-results

I wonder if it would be worth doing the same in your new docs? Maybe consider it? I think it may also play nicely if users or themes have customised the colour values a bit???

Will take a look and try to improve the documentation this way.
Nonetheless, I'm hopeful that after this PR is merged (or maybe even during the review process?), stronger CSS wizards than me will take this to the next level :)

@claremacrae
Copy link
Collaborator

claremacrae commented Jan 11, 2023

Will take a look and try to improve the documentation this way. Nonetheless, I'm hopeful that after this PR is merged (or maybe even during the review process?), stronger CSS wizards than me will take this to the next level :)

Sounds good.

I'm going to have to figure out how all this interacts with the custom task statuses work already on main. 🤞

@claremacrae
Copy link
Collaborator

I thought I'd have an play with this, so I'm trying out a few of the examples on the docs.

With this one:

/* A special color (bluish) for the 'due' component if it's for today */
.task-due.task-due-today span {
 background: #d2e8fa;
 border-radius: 10px;
 padding: 2px 8px;
}
/* A special color (reddish) for overdue due dates */
[class*="past-"] .task-due span {
 background: #ffbfcc;
 border-radius: 10px;
 padding: 2px 8px;
}

And these tasks:

- [ ] #task overdue 📅 2022-01-10
- [ ] #task overdue 📅 2023-01-10
- [ ] #task today 📅 2023-01-11

The 2nd overdue task is highlighted, but the first one isn't.

image

I wonder if it's worth a general catch-all way of colouring all overdue tasks??? Or have I missed something that is already there?

@esm7
Copy link
Contributor Author

esm7 commented Jan 11, 2023

I thought I'd have an play with this, so I'm trying out a few of the examples on the docs.

With this one:

/* A special color (bluish) for the 'due' component if it's for today */
.task-due.task-due-today span {
 background: #d2e8fa;
 border-radius: 10px;
 padding: 2px 8px;
}
/* A special color (reddish) for overdue due dates */
[class*="past-"] .task-due span {
 background: #ffbfcc;
 border-radius: 10px;
 padding: 2px 8px;
}

And these tasks:

- [ ] #task overdue 📅 2022-01-10
- [ ] #task overdue 📅 2023-01-10
- [ ] #task today 📅 2023-01-11

The 2nd overdue task is highlighted, but the first one isn't.

image

I wonder if it's worth a general catch-all way of colouring all overdue tasks??? Or have I missed something that is already there?

That's an interesting observation, the way I defined it dates that are more than 7 days ago don't get a specific class, thus are not caught by [class*="past-"] (there is even a test to verify this). Maybe it would make more sense to give them a class like past-far or something along this line.

@claremacrae
Copy link
Collaborator

That's an interesting observation, the way I defined it dates that are more than 7 days ago don't get a specific class, thus are not caught by [class*="past-"] (there is even a test to verify this). Maybe it would make more sense to give them a class like past-far or something along this line.

Yes, it was reading the docs and code, and seeing the 7 day limit, that prompted me to test this out.

Oh how I wish I had no tasks more than 7 days overdue!! 😢

@claremacrae
Copy link
Collaborator

I do like your suggestion of having a name for 'more than 7 days overdue' - and presumably similar for more than 7 days in the future...

@esm7
Copy link
Contributor Author

esm7 commented Jan 12, 2023

Fixed all of the above.

@claremacrae
Copy link
Collaborator

Thanks! I see that 3 styles have been removed from the class names table.
I think that means that this is a breaking change and will need to be called 2.0 - is that correct? Hoping I've misunderstood something.

@claremacrae
Copy link
Collaborator

Thanks! I see that 3 styles have been removed from the class names table.
I think that means that this is a breaking change and will need to be called 2.0 - is that correct? Hoping I've misunderstood something.

Looking at those classes, it's fine if it is a breaking change. The benefits are so huge.
And the kind of people likely to have modified the previous styles are likely to take advantage of the new ones!

We'd just need to figure out how to document it. (Apologies if you have done that already. I haven't reviewed the latest diffs thoroughly yet)

@claremacrae
Copy link
Collaborator

Just FYI, after this PR is merged, I plan to pull out all the CSS snippets in to separate files in the .obsidian/snippets folder in the demo vault, so that people (including me) can easily try them out. And then I have a way to embed them back in to the documentation.

Then, I'll also use an IDE to format the CSS files for consistent indentation... So don't worry about spending any time on the formatting of the CSS snippets right now...

@esm7
Copy link
Contributor Author

esm7 commented Jan 12, 2023

Thanks! I see that 3 styles have been removed from the class names table. I think that means that this is a breaking change and will need to be called 2.0 - is that correct? Hoping I've misunderstood something.

There's no breaking change here, I simply removed from this table the 3 classes that are now listed in "Basic Task Structure", to prevent duplication and confusion.

@claremacrae
Copy link
Collaborator

Ok thanks - brilliant.

@esm7
Copy link
Contributor Author

esm7 commented Jan 12, 2023

Do note however that this PR guarantees backwards-compatibility only with the default theme. Since the structure of DIVs and SPANs changed, there is no way to guarantee that CSS snippets that extensively use the old structure for their rules will continue to work. Many of them will, because the basics are the same, but it's certainly not full backwards-compatibility for any given existing CSS.

@claremacrae
Copy link
Collaborator

Hi @esm7 Apologies for the slow response on this. I've been working solid on the custom statuses feature for a couple of weeks and am desperate to release it and move on...

I've have read this code twice, though, and expect to only have very minor comments on it.

@claremacrae
Copy link
Collaborator

OK. I have released the statuses work now. I have some real world responsibilities that I need to take care of, and then I will pick this up.

@claremacrae claremacrae merged commit 95a54cc into obsidian-tasks-group:main Mar 29, 2023
@claremacrae
Copy link
Collaborator

@esm7 Very, very many thanks for your persistence with this, and this amazing work!

@esm7
Copy link
Contributor Author

esm7 commented Mar 29, 2023

Great to see this merged, I'm really looking forward to see what users will be able to do with it!

@claremacrae
Copy link
Collaborator

@esm7 Thanks to a GitHub go-slow/outage earlier, I thought I would try it out on my vault for now...

First bit of feedback is that it would be a good idea for the following to not highlight completed tasks:

/* A special color for the 'due' component if it's for today */
.task-due[data-task-due="today"] span {
background: var(--code-property);
border-radius: 10px;
padding: 2px 8px;
}
/* A special color for overdue due dates */
.task-due[data-task-due^="past-"] span {
background: var(--color-pink);
border-radius: 10px;
padding: 2px 8px;
}

(If the CSS file is modified, I will run a script that will update the same content in the docs page)

@claremacrae
Copy link
Collaborator

@esm7 Thanks to a GitHub go-slow/outage earlier, I thought I would try it out on my vault for now...

First bit of feedback is that it would be a good idea for the following to not highlight completed tasks:

I thought about suggesting 'to not highlight if there is a done date', which might be a reasonable alternative - but some users may have that facility turned off.

@claremacrae
Copy link
Collaborator

I made some progress:

/* OVERDUE - status symbol is space */

li.task-list-item.plugin-tasks-list-item[data-task-due^="past-"]:not(.is-checked) span.task-due {
    background: var(--color-pink) !important;
    border-radius: 10px !important;
    padding: 2px 8px !important;
}

li.task-list-item.plugin-tasks-list-item[data-task-scheduled^="past-"]:not(.is-checked) span.task-scheduled {
    background: var(--color-pink) !important;
    border-radius: 10px !important;
    padding: 2px 8px !important;
}

/* OVER-SCHEDULED - status symbol is space */

li.task-list-item.plugin-tasks-list-item[data-task-due="today"]:not(.is-checked) span.task-due {
    background: var(--color-yellow) !important;
    border-radius: 10px !important;
    padding: 2px 8px !important;
}

li.task-list-item.plugin-tasks-list-item[data-task-scheduled="today"]:not(.is-checked) span.task-scheduled {
    background: var(--color-yellow) !important;
    border-radius: 10px !important;
    padding: 2px 8px !important;
}

Until I tried using it on a custom-status - [!] that I have marked as TODO, but the CSS shows it as ..is-checked.

@esm7
Copy link
Contributor Author

esm7 commented Mar 30, 2023

I made some progress:

/* OVERDUE - status symbol is space */

li.task-list-item.plugin-tasks-list-item[data-task-due^="past-"]:not(.is-checked) span.task-due {
    background: var(--color-pink) !important;
    border-radius: 10px !important;
    padding: 2px 8px !important;
}

li.task-list-item.plugin-tasks-list-item[data-task-scheduled^="past-"]:not(.is-checked) span.task-scheduled {
    background: var(--color-pink) !important;
    border-radius: 10px !important;
    padding: 2px 8px !important;
}

/* OVER-SCHEDULED - status symbol is space */

li.task-list-item.plugin-tasks-list-item[data-task-due="today"]:not(.is-checked) span.task-due {
    background: var(--color-yellow) !important;
    border-radius: 10px !important;
    padding: 2px 8px !important;
}

li.task-list-item.plugin-tasks-list-item[data-task-scheduled="today"]:not(.is-checked) span.task-scheduled {
    background: var(--color-yellow) !important;
    border-radius: 10px !important;
    padding: 2px 8px !important;
}

Until I tried using it on a custom-status - [!] that I have marked as TODO, but the CSS shows it as ..is-checked.

  1. The custom statuses complicate things here a bit because indeed is-checked isn't as easy as it used to be. I think you may achieve a better result here by using the value from data-task as a filter.
  2. However, and this might be a more philosophical high-level discussion, I'm wondering whether it's healthy for us to try give users a "perfect" CSS as part of the examples. Since both of us aren't prized front-end developers or theme designers, I think it would be wiser for the example snippets to include simple examples of what can be achieved, and let users do the extra polish as they see fit. This is mostly because the price of more "perfect" examples is greatly added complexity which makes it harder to understand the basic principles of how task styling works.
  3. Taking the above a step further, what I really hope to see is an active repository of user-contributed styles. Tasks is a very popular plugin with many strong users, and we should give them the stage to share each other's styling 😎

@claremacrae
Copy link
Collaborator

  1. The custom statuses complicate things here a bit because indeed is-checked isn't as easy as it used to be. I think you may achieve a better result here by using the value from data-task as a filter.

For anyone else happening on this, there's some discussion in #1811 of using the status symbols that are in data-task for complex selections.

  1. However, and this might be a more philosophical high-level discussion, I'm wondering whether it's healthy for us to try give users a "perfect" CSS as part of the examples. Since both of us aren't prized front-end developers or theme designers, I think it would be wiser for the example snippets to include simple examples of what can be achieved, and let users do the extra polish as they see fit. This is mostly because the price of more "perfect" examples is greatly added complexity which makes it harder to understand the basic principles of how task styling works.

I'm very happy with philosophical discussions.

And I definitely agree that we don't want perfect styling in the docs.

What I am after is being able to give non-CSS speakers enough information that they can experiment until they produce something that is good enough for their needs, without them needing to bother me on user support.

(Which would in turn result in me bothering you to help with user support! 🤣)

In case you were thinking 'but we cannot write a whole text book on CSS', I also agree with that, but when releasing a feature for a tool that many non-developers (and non-CSS developers) will use, giving them patterns that they can use and edit is very powerful indeed.

(Hence all the effort I put in to the Boolean docs - knowing the pitfalls and confusions of Boolean logic for non-devs, from my former career).

And because all my own CSS efforts have been produced exactly by this sort of trial and error, if I can't use the styling for basic every-day things that I would like in my vault, I am reasonably confident multiple other users will request the same thing.

Hence my wishing to have starting-point documentation to point them to.

  1. Taking the above a step further, what I really hope to see is an active repository of user-contributed styles. Tasks is a very popular plugin with many strong users, and we should give them the stage to share each other's styling 😎

I agree, but let's see what the user-support load is like for this version so far.

@esm7
Copy link
Contributor Author

esm7 commented Mar 30, 2023

Sounds good.
I do think however that a space for users to share themes and theming tips (maybe a Discord channel or part of an existing channel?) could be useful from day 1, also in the context of reducing support load. I'll happily do my best in answering anything sent towards me, but even just for giving good starting points for users (which I surely agree with!), I think it will serve a great purpose.

@claremacrae
Copy link
Collaborator

claremacrae commented Mar 30, 2023

I could point people at Show&Tell (or whatever it’s called) in Discussions for now?

@esm7
Copy link
Contributor Author

esm7 commented Mar 30, 2023

I could people at Show&Tell (or whatever it’s called) in Discussions for now?

I think this would be awesome.

@claremacrae
Copy link
Collaborator

I could people at Show&Tell (or whatever it’s called) in Discussions for now?

I think this would be awesome.

Great. I’ve added it to my list!

Thank you, as ever, for being so constructively helpful!

@aubreyz
Copy link

aubreyz commented Apr 9, 2023

This is a general comment for now but perhaps I could turn it into an issue. I have been away for a short while and returned to the truly amazing CSS developments - thank you greatly @esm7 and @claremacrae

It seems to me there is some potential for confusion between the statuses CSS and any additional task CSS snippet I might install (and some potential for consolidation in the documentation). We are encouraged (asked) to install a snippet for statuses.
I installed the very nice looking SlRvb's. However this does a lot of other stuff beyond simply changing the checkbox icon - to fonts, colors and so on -- which I then go on and control separately with other css.

This is a tad confusing - and how do we even know the order in which the css snippets are going to load??

What is missing from the "Styling Tasks" Documentation is an example of changing the checkbox icon (and I am not sure how to extract that myself from the 1000 line SlRvb css) - but that must be possible beyond applying a nice glowing halo. There are nice examples of changing other things based on the Status, but not the checkbox itself. For people who want better control, why not give an example of that, and then they can have just one css file both for (the three or 4 instead of 50) statuses they want and other css customisation.

At the moment I am needing conflicting css between two files to achieve what I want.

@claremacrae
Copy link
Collaborator

Hi @aubreyz,

Many thanks, and I do agree with all that you say.

And when I have time, I am migrating my own vault away from SlRvb to my own snippets, and indeed trying to figure out how to style the checkbox directly with Tasks myself.

Feel free to set up a separate feature request for the missing documentation you mentioned below...

Better still, if you make any progress on information that is worth sharing with other users, please add it to the same issue and we can get it written up.

I do want to say for the record, though, that when I rebased 3.0.0 I was aware of the need for custom status docs to say how to use the Tasks styling - but at the time I just didn't know how to do that, and besides, I felt like getting 3.0.0 out would give me a chance to have a short break from Tasks.

And we needed to hear back from users about how they are styling Tasks in order to help write that documentation.

(So far only myself and @esm7 have done shared our styles though.)

What is missing from the "Styling Tasks" Documentation is an example of changing the checkbox icon (and I am not sure how to extract that myself from the 1000 line SlRvb css).

I agree. If you find out, please let me know.

(I am not a CSS developer...)

@TomWol
Copy link

TomWol commented Apr 11, 2023

I have been testing the release version and it's working great, again thank you so much for the hard work @esm7 .

A few notes - not sure whether to continue posting them here or create separate issues for them... Sorry in advance if at this stage they should've been posted as issues, do let me know and I will correct.

Hidden components

I've noticed that not all hidden components added via hide are represented in the code, for example hide backlink does not generate a tasks-layout-hide-backlink class the way a hide due date generates tasks-layout-hide-dueDate for the ul element... These are really convenient for intelligently adapting and compacting a grid layout like the one provided in the docs.
(Also might be a case for data-attributes rather than classes? The way data-task-group-by is aswell, a data-task-hide or something.)

Group by

Speaking of the data-task-group-by attribute, I have been testing this out thoroughly and found that it works in most cases. When trying to use it to detect specific group by headings, I have to write a few rules to catch them all though, and I think I can only reliablly identify 3 levels of group by headings.
For example, below I make use of the CSS attribute selectors to correctly deduce the position of a due heading in any combination of group by statements:

/* This first line covers all instances of the due heading being an h4 (only h4, with h5 and with h5 and h6) */
h4.tasks-group-heading:has(~ ul.plugin-tasks-query-result[data-task-group-by^="due"]),
/* This second line covers all instances of the heading being the last (technically also h4, h5 preceded by h4, h6 preceded by h4 and h5) */
.tasks-group-heading:has(+ ul.plugin-tasks-query-result[data-task-group-by$="due"]),
/* This third line covers the only remaining instance, (h5 in between h4 and h6) */
h5.tasks-group-heading:has(~ ul.plugin-tasks-query-result[data-task-group-by*=",due,"]) { 
    background: var(--tasks-color-heading-background-due);
    border-color: var(--tasks-color-heading-border-due);
}

When adding more than 3 group by layers, I don't think I would be able to uniquely identify [data-task-group-by*=",due,"]in the last rule. Really, I don't see any point in using more than 3group by` headings myself, I think that convolutes the layout in many ways... But I wanted to point it out.

I do still feel that adding a specific data-task-group-by attribute to the actual headings themselves rather than the ul element would be easier to build styles off of, though. I tried to correctly identify a High Priority heading and had to write the following rules:

/* As opposed to the earlier examples targetting specific headings, we need more exact CSS rules targetting the 4 variants of a priority heading. So we need to actually observe the following cases... */
/* This one targets all priority headings that are directly followed by a task list (so the last group by statement). This can be h4, h5 and h6, it just has to be the last one. */
.tasks-group-heading:has(+ ul.plugin-tasks-query-result[data-task-group-by$="priority"] li[data-task-priority="high"]), 
/* Next we target h5 headings that are the middle group by statement (so are followed by an h6 and then the task list) */
h5.tasks-group-heading:has(+ h6.tasks-group-heading + ul.plugin-tasks-query-result[data-task-group-by*=",priority,"] > li[data-task-priority="high"]),
/* Third we need to target the h4 headings that are the first of 2 group by headings (so are followed by an h5 and then the task list) */
h4.tasks-group-heading:has(+ h5.tasks-group-heading + ul.plugin-tasks-query-result[data-task-group-by^="priority"] > li[data-task-priority="high"]), 
/* Finally we need to target the h4 headings that are the first of 3 group by headings (so are followed by an h5, an h6 and then the task list). */
h4.tasks-group-heading:has(+ h5.tasks-group-heading + h6.tasks-group-heading + ul.plugin-tasks-query-result[data-task-group-by^="priority"] > li[data-task-priority="high"]) {
    border-color: var(--tasks-color-priority-high);
}

And that's just for one priority instance. But it works, which is already really nice. Before I had to use specific cssclasses in the YAML frontmatter to sort of define for each file which headings were in which order. Now I can just target them accurately, so thanks for that.

@TomWol
Copy link

TomWol commented Apr 11, 2023

One thing I also just noticed: both taskand tasks prefixes are used for classes inside a task li element.

Inside the li element, a task's children have the following classes:

  • task-list-item-checkbox (input)
  • tasks-list-text (span, notice the plural tasks)
  • task-extras(span, back to singular task).

Inside the two spans, we have the following classes (I'm probably missing some, mostly just to illustrate my point):

  • task-description
  • task-priority
  • task-recurring
  • task-done
  • tasks-backlink (back to plural)
  • tasks-edit (also back to plural)

I feel like these should all be task, what do you think @esm7?

@claremacrae
Copy link
Collaborator

Hi Tom, thanks for your suggestions, but we have no way of tracking them in a closed PR.

Feel free to make new suggestions - as bugs or feature requests, as appropriate - or discussions if they need discussion.

Try to keep each issue or FR to broadly a single topic, so that they can be acted upon independently and closed when done.

@esm7 if you see this in time, please hold off answering until these are in a location we can track. Cheers.

@TomWol
Copy link

TomWol commented Apr 11, 2023

Done, thanks for clarifying @claremacrae.

@obsidian-tasks-group obsidian-tasks-group locked as resolved and limited conversation to collaborators Apr 11, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
scope: css styling Changes to styling of elements displayed by Tasks, including search results and individual tasks type: enhancement New feature or request
Projects
Status: 🎉 Released
Development

Successfully merging this pull request may close these issues.

None yet

4 participants