Skip to content

Commit

Permalink
Converted some CSS classes to data attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
esm7 committed Mar 19, 2023
1 parent 551bcc9 commit c2efb51
Show file tree
Hide file tree
Showing 9 changed files with 19,725 additions and 264 deletions.
95 changes: 47 additions & 48 deletions docs/advanced/styling.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ has_toc: false

# Styling Tasks

In rendered queries and Reading View, the Tasks plugin adds detailed CSS classes that represent many of each task's content, to allow for very extensive styling options via CSS.
Not only each component in a rendered task line is tagged with classes to differentiate it, many components also add classes that represent the actual content of the task, so CSS rules can refer to data such as the relative due date of a task or its specific priority.
In rendered queries and Reading View, the Tasks plugin adds detailed CSS classes and data attributes that represent many of each task's content, to allow for very extensive styling options via CSS.
Not only each component in a rendered task line is tagged with classes to differentiate it, many components also add classes and data attributes that represent the actual content of the task, so CSS rules can refer to data such as the relative due date of a task or its specific priority.

## Basic Task Structure

Expand All @@ -21,17 +21,17 @@ The Tasks plugin renders a task in the following structure (this refers to query
```markdown
- Obsidian code block (div class="block-language-tasks")
- Results list (ul class="plugin-tasks-query-result") OR Reading View list (ul class="contains-task-list")
- Task (li class="task-list-item" + specific classes like "tasks-tag-atHome tasks-priority-medium tasks-due-past-1d" + data-task="[custom_status]" + data+line="[line]")
- Task (li class="task-list-item" + attributes like data-task-priority="medium" data-task-due="past-1d" + data-task="[custom_status]" + data-line="[line]")
- Task checkbox (li class="task-list-item-checkbox")
- Task content (span class="tasks-list-text")
- Task description and tags (span class="task-description" + tag specific classes)
- Task description and tags (span class="task-description")
- Internal span
- Each tag in the description is wrapped in <a href class="tag" + tag specific classes>
- Task priority (span class="task-priority" + priority specific classes)
- Each tag in the description is wrapped in <a href class="tag" data-tag-name="[tag-name]">
- Task priority (span class="task-priority" + data-task-priority attribute)
- Internal span
- Task recurrence rule (span class="task-recurring")
- Internal span
- Task start date (span class="task-start" + date specific classes)
- Task start date (span class="task-start" + data-task-start attribute)
- Internal span
- ... scheduled date, due date and done date in this order
- Task extras (link, edit button) (span class="task-extras")
Expand All @@ -41,15 +41,15 @@ The Tasks plugin renders a task in the following structure (this refers to query
As can be seen above, the basic task `li` contains a checkbox and a content span.
The content span contains a list of **component** spans: description, priority, recurrence, start date, scheduled date, due date and done date in this order.

Each component span is marked with a **generic class**, which denotes the type of the component, and in some cases a **specific class** that represents the component's content itself.
Each component span is marked with a **generic class**, which denotes the type of the component, and in some cases a **data attributes** that represents the component's content itself.

Within each component span there is an additional "internal" span, which is the one holding the actual component text.
The reason for this additional internal span is that it allows CSS styles that closely wrap the text itself, rather than its container box, e.g. for the purpose of drawing a highlight or a box that is exactly in the size of the text.

## Generic and Specific Classes
## Generic Classes and Data Attributes

{: .released }
Specific classes were introduced in Tasks X.Y.Z.
Data attributes were introduced in Tasks X.Y.Z.

Each rendered task component (description, priority, recurrence rule etc) includes a **generic class** that denotes this type of component.
The generic classes are:
Expand All @@ -62,49 +62,38 @@ The generic classes are:
- `task-done`
- `task-recurring`

In addition to the generic classes, there are **specific classes** that represent the content of the various task components.
In addition to the generic classes, there are [**data attributes**](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes) that represent the content of the various task components.

A **priority specific class** represents the specific priority of a class. It can be one of the following:
A **priority data attributes** named `data-task-priority` represents the specific priority of a class. It can be `high`, `medium`, `low` or `none`.

- `task-priority-high`
- `task-priority-medium`
- `task-priority-low`
- `task-priority-none`
A **date attribute** represents a due, start, scheduled or done date in a format relative to the current date.
The date attributes are `data-task-due`, `data-task-start`, `data-task-scheduled` and `data-task-done` and are populated with a relative expression that denotes the number of days this field represents compared to today:

A **date specific class** represents a due, start, scheduled or done date in a format relative to the current date.
It starts with `task-due-`, `task-start-`, `task-scheduled-` or `task-done-` followed by a relative expression that denotes the number of days this field represents compared to today:
- `data-task-due="today"` (or `data-task-start="today"`, `data-task-start="today"` etc) represents today.
- `data-task-due="future-1d"` (or `data-task-start="future-1d"`) represents 1 day in the future, i.e. tomorrow.
- `data-task-due="past-1d"` (or `data-task-start="past-1d"`) represents 1 day in the past, i.e. yesterday.
- These attributes are added up to 7 days in the future or past, e.g. `data-task-scheduled="future-7d"` or `data-task-due="past-7d"`.
- Dates that are further than 7 days in the future or past are given a `far` postfix, e.g. `data-task-scheduled="future-far"` or `data-task-due="past-far"`.

- `task-due-today` (or `task-start-today`, `task-done-today` etc) represents today.
- `task-due-future-1d` (or `task-start-future-1d`) represents 1 day in the future, i.e. tomorrow.
- `task-due-past-1d` (or `task-start-past-1d`) represents 1 day in the past, i.e. yesterday.
- These specific classes are added up to 7 days in the future or past, e.g. `task-scheduled-future-7d` or `task-due-past-7d`.
- Dates that are further than 7 days in the future or past are given a `far` postfix, e.g. `task-scheduled-future-far` or `task-due-past-far`.
A **tag data attribute** repeats each tag's content as a data attribute, for the purpose of applying formatting according to specific tags.
The tag `<a>` elements are added a `data-tag-name` attribute with a *sanitized* version of the tag name, which basically means that characters that are illegal to use in HTML attributes (e.g. `&`, `"`) are replaced with dashes.

A **tag specific class** translates each task tag into a CSS class, for the purpose of applying formatting according to specific tags.
It starts with `task-tag-` followed by a *sanitized* version of the tag name, which basically means that characters that are illegal to use in CSS class names are replaced by dashes.
(CSS class names allow only alphanumeric characters, a dash and an underscore.)
Examples:
Data attributes are added to both their corresponding components (e.g. to the due date component) and also to the complete task `li`, to make it easy for a CSS rule to style a complete task according to some property (e.g. color differently the complete task if it's due today, color a task according to a tag) or just one relevant component.

- A task with the tag `#phone` will be added with the specific class `task-tag-phone`.
- A task with the tag `#t/easy` will be added with the specific class `task-tag-t-easy`.
- A task with the tag `#task/atHome` will be added the specific class `task-tag-task-atHome`.
An exception is the tag data attribute which is added only to the tag's `<a>` element within the rendered description -- however you can still use a CSS `:has` selector to format an entire task's description according to a tag, as demonstrated in the examples below.

Note that tag specific classes are also added to the tag `<a>` element within the rendered description.

Specific classes are added to both their corresponding components (e.g. to the due date component) and also to the complete task `li`, to make it easy for a CSS rule to style a complete task according to some property (e.g. color differently the complete task if it's due today, color a task according to a tag) or just one relevant component.

**Tip:** [CSS wildcard selectors](https://www.geeksforgeeks.org/wildcard-selectors-and-in-css-for-classes/) are a good way to select all past dates or future dates at once -- just use `[class*="past-"] .task-due ...` to address all overdue tasks, for example.
**Tip:** [CSS wildcard selectors](https://www.geeksforgeeks.org/wildcard-selectors-and-in-css-for-classes/) are a good way to select all past dates or future dates at once -- just use `.task-due[data-task-due^="past-"]` to address all overdue tasks, for example. Examples that utilize this can be found below.

## Hidden Components, Groups & Short Mode

**Hidden components**, e.g. a `hide priority` line in a query, will generate the following:

- The query container (`class="plugin-tasks-query-result"`) will include a `tasks-layout-hide...` class, e.g. `tasks-layout-hide-priority`.
- Although the priority will not be rendered in the query, the upper task element (`li class="task-list-item"`) will still be added the specific class of hidden components, e.g. `task-priority-high`.
- Although the priority will not be rendered in the query, the upper task element (`li class="task-list-item"`) will still be added the attribute of hidden components, e.g. `data-task-priority="high"`.

**Short mode** will add a `tasks-layout-short-mode` class to the query container.

**Grouping rules** will add `tasks-group-by...` classes to the query container, e.g. `tasks-group-by-due`.
**Grouping rules** will add a `data-task-group-by` attribute to the query container, e.g. `data-task-group-by="due,scheduled"`.

## More Classes

Expand Down Expand Up @@ -149,15 +138,15 @@ Making tags, internal links and the recurrence rules of tasks to appear in gray:
The following rules remove the Tasks priority emoticon and render the tasks' checkboxes in red, blue and orange according to the tasks' priority:

```css
.task-priority-high input[type=checkbox] {
.task-list-item[data-task-priority="high"] input[type=checkbox] {
box-shadow: 0px 0px 2px 2px var(--color-red);
border-color: var(--color-red);
}
.task-priority-low input[type=checkbox] {
.task-list-item[data-task-priority="low"] input[type=checkbox] {
box-shadow: 0px 0px 2px 2px var(--color-blue);
border-color: var(--color-blue);
}
.task-priority-medium input[type=checkbox] {
.task-list-item[data-task-priority="medium"] input[type=checkbox] {
box-shadow: 0px 0px 2px 2px var(--color-orange);
border-color: var(--color-orange);
}
Expand All @@ -173,13 +162,13 @@ The following rules mark 'today' due dates as blue and past due dates as red:

```css
/* A special color for the 'due' component if it's for today */
.task-due.task-due-today span {
.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[class*="past-"] span {
.task-due[data-task-due^="past-"] span {
background: var(--color-pink);
border-radius: 10px;
padding: 2px 8px;
Expand All @@ -191,11 +180,21 @@ The following rules mark 'today' due dates as blue and past due dates as red:
The following rule adds a green glow around `#task/atHome` tags inside the description:

```css
a.tag.task-tag-task-atHome {
a.tag[data-tag-name="#task/atHome"] {
box-shadow: 0 0 5px green;
}
```

The following rule adds a rounded red background to the description of a task if it contains the tag `#task/strategic`:

```css
.task-description span:has(.tag[data-tag-name="#task/strategic"]) {
background: #ffbfcc;
border-radius: 10px;
padding: 2px 8px;
}
```

### Circle Checkboxes

The following renders checkboxes as circles instead of squares:
Expand Down Expand Up @@ -286,15 +285,15 @@ span.tasks-list-text {
}

/* Represent tasks' priority with colorful round checkboxes instead of the priority emoticons */
.task-priority-high input[type=checkbox] {
.task-list-item[data-task-priority="high"] input[type=checkbox] {
box-shadow: 0px 0px 2px 2px var(--color-red);
border-color: var(--color-red);
}
.task-priority-low input[type=checkbox] {
.task-list-item[data-task-priority="low"] input[type=checkbox] {
box-shadow: 0px 0px 2px 2px var(--color-blue);
border-color: var(--color-blue);
}
.task-priority-medium input[type=checkbox] {
.task-list-item[data-task-priority="medium"] input[type=checkbox] {
box-shadow: 0px 0px 2px 2px var(--color-orange);
border-color: var(--color-orange);
}
Expand All @@ -304,13 +303,13 @@ span.task-priority {
}

/* A special color for the 'due' component if it's for today */
.task-due.task-due-today span {
.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[class*="past-"] span {
.task-due[data-task-due^="past-"] span {
background: var(--color-pink);
border-radius: 10px;
padding: 2px 8px;
Expand Down
19,548 changes: 19,500 additions & 48 deletions resources/sample_vaults/Tasks-Demo/.obsidian/plugins/obsidian-tasks-plugin/main.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.block-language-tasks:has(ul.plugin-tasks-query-result.tasks-group-by-priority) .tasks-group-heading {
.block-language-tasks:has(ul.plugin-tasks-query-result[data-task-group-by="priority"]) .tasks-group-heading {
color: red;
}

Expand Down
5 changes: 3 additions & 2 deletions src/Commands/CreateOrEdit.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { App, Editor, MarkdownView, View } from 'obsidian';
import { App, Editor, MarkdownView } from 'obsidian';
import type { MarkdownFileInfo } from 'obsidian';
import { TaskModal } from '../TaskModal';
import type { Task } from '../Task';
import { DateFallback } from '../DateFallback';
import { taskFromLine } from './CreateOrEditTaskParser';

export const createOrEdit = (checking: boolean, editor: Editor, view: View, app: App) => {
export const createOrEdit = (checking: boolean, editor: Editor, view: MarkdownView | MarkdownFileInfo, app: App) => {
if (checking) {
return view instanceof MarkdownView;
}
Expand Down
5 changes: 3 additions & 2 deletions src/Commands/ToggleDone.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Editor, MarkdownView, View } from 'obsidian';
import { Editor, MarkdownView } from 'obsidian';
import type { MarkdownFileInfo } from 'obsidian';
import { StatusRegistry } from '../StatusRegistry';

import { Task, TaskRegularExpressions } from '../Task';

export const toggleDone = (checking: boolean, editor: Editor, view: View) => {
export const toggleDone = (checking: boolean, editor: Editor, view: MarkdownView | MarkdownFileInfo) => {
if (checking) {
if (!(view instanceof MarkdownView)) {
// If we are not in a markdown view, the command shouldn't be shown.
Expand Down
4 changes: 2 additions & 2 deletions src/Commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { App, Editor, Plugin, View } from 'obsidian';
import type { App, Editor, MarkdownFileInfo, MarkdownView, Plugin } from 'obsidian';
import { createOrEdit } from './CreateOrEdit';

import { toggleDone } from './ToggleDone';
Expand All @@ -17,7 +17,7 @@ export class Commands {
id: 'edit-task',
name: 'Create or edit task',
icon: 'pencil',
editorCheckCallback: (checking: boolean, editor: Editor, view: View) => {
editorCheckCallback: (checking: boolean, editor: Editor, view: MarkdownView | MarkdownFileInfo) => {
return createOrEdit(checking, editor, view, this.app);
},
});
Expand Down
13 changes: 6 additions & 7 deletions src/QueryRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ class QueryRenderChild extends MarkdownRenderChild {
const taskList = content.createEl('ul');
taskList.addClasses(['contains-task-list', 'plugin-tasks-query-result']);
taskList.addClasses(layout.specificClasses);
// TODO TEMP add tests
taskList.addClasses(this.getGroupingClasses());
const groupingAttribute = this.getGroupingAttribute();
if (groupingAttribute && groupingAttribute.length > 0) taskList.dataset.taskGroupBy = groupingAttribute;
for (let i = 0; i < tasksCount; i++) {
const task = tasks[i];
const isFilenameUnique = this.isFilenameUnique({ task });
Expand Down Expand Up @@ -359,12 +359,11 @@ class QueryRenderChild extends MarkdownRenderChild {
return allFilesWithSameName.length < 2;
}

private getGroupingClasses() {
const classes: string[] = [];
private getGroupingAttribute() {
const groupingRules: string[] = [];
for (const group of this.query.grouping) {
const className = `tasks-group-by-${group.property}`;
classes.push(className);
groupingRules.push(group.property);
}
return classes;
return groupingRules.join(',');
}
}
Loading

0 comments on commit c2efb51

Please sign in to comment.