Skip to content

Commit

Permalink
Update how tags are rendered to allow for custom styling (#1179)
Browse files Browse the repository at this point in the history
* Updated how tags are rendered to allow for custom styling via space-styles

* Wrapped the existing span for a tag with an anchor element <a> to
improve ux for screen readers
* Added data-tag-name attribute to tags so that given a tag `#my-tag` it
will have the attribute `data-tag-name="my-tag"` added to the new `<a>`
element wrapper allowing for end users to target tags for styling with a
css selector such as `.sb-hashtag[data-tag-name="my-tag"]{...}`

* Updated Docs
  • Loading branch information
terr-steak authored Dec 14, 2024
1 parent a56db78 commit 2020e85
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 32 deletions.
16 changes: 7 additions & 9 deletions common/markdown_parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -584,15 +584,13 @@ const NakedURL = regexParser(
},
);

const Hashtag = regexParser(
{
firstCharCode: 35, // #
regex: new RegExp(`^${tagRegex.source}`),
nodeType: "Hashtag",
className: "sb-hashtag",
tag: ct.HashtagTag,
},
);
const Hashtag = regexParser({
firstCharCode: 35, // #
regex: new RegExp(`^${tagRegex.source}`),
nodeType: "Hashtag",
className: "sb-hashtag-text",
tag: ct.HashtagTag,
});

const TaskDeadline = regexParser({
firstCharCode: 55357, // 📅
Expand Down
14 changes: 9 additions & 5 deletions plugs/markdown/markdown_render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { Fragment, renderHtml, type Tag } from "./html_render.ts";
import { isLocalPath } from "@silverbulletmd/silverbullet/lib/resolve";
import type { PageMeta } from "@silverbulletmd/silverbullet/types";
import * as TagConstants from "../../plugs/index/constants.ts";

export type MarkdownRenderOptions = {
failOnUnknown?: true;
Expand Down Expand Up @@ -335,15 +336,18 @@ function render(
body: url,
};
}
case "Hashtag":
case "Hashtag": {
const tagText: string = t.children![0].text!;
return {
name: "span",
name: "a",
attrs: {
class: "hashtag",
class: "hashtag sb-hashtag",
"data-tag-name": tagText.replace("#", ""),
href: `/${TagConstants.tagPrefix}${tagText.replace("#", "")}`,
},
body: t.children![0].text!,
body: tagText,
};

}
case "Task": {
let externalTaskRef = "";
collectNodesOfType(t, "WikiLinkPage").forEach((wikilink) => {
Expand Down
2 changes: 2 additions & 0 deletions web/cm_plugins/clean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { fencedCodePlugin } from "./fenced_code.ts";
import { frontmatterPlugin } from "./frontmatter.ts";
import { cleanEscapePlugin } from "./escapes.ts";
import { luaDirectivePlugin } from "./lua_directive.ts";
import { hashtagPlugin } from "./hashtag.ts";

export function cleanModePlugins(client: Client) {
return [
Expand Down Expand Up @@ -46,5 +47,6 @@ export function cleanModePlugins(client: Client) {
cleanCommandLinkPlugin(client),
cleanEscapePlugin(),
luaDirectivePlugin(client),
hashtagPlugin(),
] as Extension[];
}
43 changes: 43 additions & 0 deletions web/cm_plugins/hashtag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { syntaxTree } from "@codemirror/language";
import { Decoration } from "@codemirror/view";
import { decoratorStateField } from "./util.ts";
import * as Constants from "../../plugs/index/constants.ts";
import { extractHashtag } from "../../plug-api/lib/tags.ts";

export function hashtagPlugin() {
return decoratorStateField((state) => {
const widgets: any[] = [];

syntaxTree(state).iterate({
enter: ({ type, from, to }) => {
if (type.name !== "Hashtag") {
return;
}

const tag = state.sliceDoc(from, to);

if (tag.length === 1) {
// Invalid Hashtag, a length of 1 means its just #
return;
}

const tagName = extractHashtag(tag);

// Wrap the tag in html anchor element
widgets.push(
Decoration.mark({
tagName: "a",
class: "sb-hashtag",
attributes: {
href: `/${Constants.tagPrefix}${tagName}`,
rel: "tag",
"data-tag-name": tagName,
},
}).range(from, to),
);
},
});

return Decoration.set(widgets, true);
});
}
2 changes: 1 addition & 1 deletion web/cm_plugins/hide_mark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export function hideHeaderMarkPlugin() {
widgets.push(
Decoration.mark({
tagName: "span",
class: "sb-hashtag",
class: "sb-hashtag-text",
}).range(from, from + 1),
);

Expand Down
2 changes: 1 addition & 1 deletion web/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default function highlightStyles() {
{ tag: t.processingInstruction, class: "sb-meta" },
{ tag: t.punctuation, class: "sb-punctuation" },
{ tag: ct.HorizontalRuleTag, class: "sb-hr" },
{ tag: ct.HashtagTag, class: "sb-hashtag" },
{ tag: ct.HashtagTag, class: "sb-hashtag-text" },
{ tag: ct.NakedURLTag, class: "sb-naked-url" },
{ tag: ct.TaskDeadlineTag, class: "sb-task-deadline" },
{ tag: ct.NamedAnchorTag, class: "sb-named-anchor" },
Expand Down
6 changes: 5 additions & 1 deletion web/styles/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
}

// If a header only contains a tag, it's likely a line containging "#" which may turn into a hashtag, so style it as such instead of a header
.sb-line-h1:has(> span.sb-hashtag:only-child) {
.sb-line-h1:has(> a.sb-hashtag:only-child) {
font-size: 1em;
padding: 0;
font-weight: normal;
Expand Down Expand Up @@ -276,6 +276,10 @@
font-size: 0.9em;
}

a.sb-hashtag {
text-decoration: none;
}

.sb-strikethrough {
text-decoration: line-through;

Expand Down
53 changes: 38 additions & 15 deletions website/Objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,32 @@ Beside these, any number of additional tag-specific and custom [[Attributes]] ca
# Tags
Every object has a main `tag`, which signifies the type of object being described. In addition, any number of additional tags can be assigned as well via the `tags` attribute. You can use either the main `tag` or any of the `tags` as query sources in [[Live Queries]] — examples below.

Here are the currently built-in tags:

## page
## Styling
You can add custom styles to a tag by leveraging the `data-tag-name` attribute, [CSS Attribute Selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors) and custom [[Speace Style]]'s. Every tag gets an attribute added to it called `data-tag-name` that is set to the tag name with the `#` symbol stripped out. So given the tag #my-cool-tag the `data-tag-name` attribute would look like:
`data-tag-name="my-cool-tag"`.
This allows us to do things like change the color of the #my-cool-tag to have a purple background, limegreen text and bold font by adding the following [[Space Style]]:
```css
.sb-hashtag[data-tag-name="my-cool-tag"] {
background: purple;
color: limegreen;
font-weight: bolder;
}
```
Additionally tags written using angle brackets, such as...
#<my cool tag>
...can be styled via [[Space Style]] like this:
```css
.sb-hashtag[data-tag-name="my cool tag"] {
background: purple;
color: limegreen;
font-weight: bolder;
}
```


## Built-in tags

### page
Every page in your space is available via the `page` tag. You can attach _additional_ tags to a page, by either specifying them in the `tags` attribute [[Frontmatter]], or by putting additional [[Tags]] in a stand alone paragraph with no other (textual) content in them, for instance check the very first line of this page that says `#level/intermediate`.

In addition to `ref` and `tags`, the `page` tag defines a bunch of additional attributes as can be seen in this example query:
Expand All @@ -39,15 +62,15 @@ Note that you can also query this page using the `level/intermediate` directly:
level/intermediate
```

## aspiring-page
### aspiring-page
[[Aspiring Pages]] are pages that are linked to, but not yet created.

```query
aspiring-page
```


## table
### table
Markdown table rows are indexed using the `table` tag, any additional tags can be added using [[Tags]] in any of its cells.

| Title | Description Text |
Expand All @@ -62,7 +85,7 @@ table

Table headers will be normalized by converting them to lowercase and replacing all non alphanumeric characters with `_`.

## item
### item
List items (both bullet point and numbered items) are indexed with the `item` tag, and additional tags can be added using [[Tags]].

Here is an example of a #quote item using a custom [[Attributes|attribute]]:
Expand Down Expand Up @@ -108,7 +131,7 @@ upnext render [[Library/Core/Query/Task]]
Similar to [[#item]], `task` objects have a `parent` attribute when nested (pointing to their parent `item`), and inherit their ancestor’s tags in `itags`.


## taskstate
### taskstate
[[Plugs/Tasks]] support the default `x` and ` ` states (done and not done), but custom states as well. Custom states used across your space are kept in `taskstate`:

* [NOT STARTED] Task 1
Expand Down Expand Up @@ -150,7 +173,7 @@ Which then becomes queriable via the `person` tag:
person
```

## link
### link
All page _links_ are tagged with `link`. You cannot attach additional tags to links. The main two attributes of a link are:

* `toPage` the page the link is linking _to_
Expand All @@ -166,7 +189,7 @@ Here is a query that shows some links that appear in this particular page:
link where page = @page.name limit 5
```

## anchor
### anchor
[[Markdown/Anchors]] use the $myanchor notation to allow deeplinking into a page and are also indexed and queryable. It is not possible to attach additional tags to an anchor.

Here is an example query:
Expand All @@ -175,15 +198,15 @@ Here is an example query:
anchor where page = @page.name
```

## header
### header
Headers (lines starting with `#`, `##` etc.) are indexed as well and queriable.

```query
header where page = @page.name limit 3
```


## tag
### tag
The ultimate meta tag is _tag_ itself, which indexes for all tags used, in which page they appear and what their “parent tag” is (the context of the tag: either `page`, `item` or `task`).

Here are the tags used/defined in this page:
Expand All @@ -192,24 +215,24 @@ Here are the tags used/defined in this page:
tag where page = @page.name select name, parent
```

## space-config
### space-config
This stores all configuration picked up as part of [[Space Config]]

```query
space-config select key
```


# System tags
## System tags
The following tags are technically implemented a bit differently than the rest, but they are still available to be queried.

## command
### command
Enables querying of all [[Commands]] available in SilverBullet as well as their assigned keyboard shortcuts.
```query
command order by name limit 5
```

## syscall
### syscall
Enables querying of all [[PlugOS]] syscalls enabled in your space. Mostly useful in the context of [[Plugs]] and [[Space Script]] development.

```query
Expand Down
20 changes: 20 additions & 0 deletions website/Space Style.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,26 @@ button {
button:hover {
/* box-shadow: #121212 0 0 0 3px, transparent 0 0 0 0; */
}
/* Add custom styling to a specific tag
--> Example Tag: #my-cool-tag */
.sb-hashtag[data-tag-name="my-cool-tag"] {
background: purple;
color: limegreen;
font-weight: bolder;
}
/* --> Example Tag with angle brackets: #<my cool tag> */
.sb-hashtag[data-tag-name="my cool tag"] {
background: purple;
color: limegreen;
font-weight: bolder;
}
/* Custom Styling to all tags
.sb-hashtag[data-tag-name] {
background: yellow;
color: goldenrod;
border: 2px dashed goldenrod; */
}
```

Another example can be found in [[Page Decorations#Use case: pimp my page]]

0 comments on commit 2020e85

Please sign in to comment.