diff --git a/src/css/base/properties.css b/src/css/base/properties.css
index 472edf2..42a751c 100644
--- a/src/css/base/properties.css
+++ b/src/css/base/properties.css
@@ -26,4 +26,5 @@
--button-border: url('data:image/svg+xml;utf8,');
--button-border-hover: url('data:image/svg+xml;utf8,');
--button-border-danger: url('data:image/svg+xml;utf8,');
+ --button-rss: url('data:image/svg+xml;utf8,');
}
diff --git a/src/css/base/typography.css b/src/css/base/typography.css
index c9207d9..e0bcf86 100644
--- a/src/css/base/typography.css
+++ b/src/css/base/typography.css
@@ -13,7 +13,7 @@ p:has(small:read-only) {
font-size: var(--font-size-2);
}
-a:where([href]) {
+a:where([href]:not([class])) {
--anchor-underline-color: oklch(from currentColor l c h / 0.6);
--anchor-underline-offset: 0.2em;
--anchor-underline-thickness: 2px;
diff --git a/src/css/components/bookmark.css b/src/css/components/bookmark.css
index fadec68..398b8bf 100644
--- a/src/css/components/bookmark.css
+++ b/src/css/components/bookmark.css
@@ -9,7 +9,11 @@
flex-wrap: wrap;
justify-content: space-between;
- & button {
+ & > button {
+ margin-block: -9px;
+ }
+
+ & > div:has(button, .Button) {
margin-block: -9px;
}
}
diff --git a/src/css/components/button.css b/src/css/components/button.css
index 9e10a8b..d06b093 100644
--- a/src/css/components/button.css
+++ b/src/css/components/button.css
@@ -1,15 +1,20 @@
+.Button,
button[type] {
+ --block-size: calc(var(--font-size-button) + 30px);
border: 15px solid transparent;
border-image: var(--button-border) 15 fill stretch;
- block-size: calc(var(--font-size-button) + 30px);
+ block-size: var(--block-size);
color: rgb(var(--color-black));
+ display: block;
font-family: var(--font-family-2);
font-size: var(--font-size-button);
font-weight: 400;
inline-size: fit-content;
line-height: 1;
+ min-inline-size: var(--block-size);
padding: 0 5px;
- text-box: trim-both ex alphabetic;
+ /*text-box: trim-both ex alphabetic;*/
+ text-decoration: none;
text-transform: uppercase;
text-shadow: 2px 2px rgb(var(--color-brown) / 0.3);
transition: border-image 100ms;
@@ -36,3 +41,20 @@ button[type] {
block-size: calc(var(--font-size-button) + 20px);
}
}
+
+.Button-rss {
+ position: relative;
+
+ &:not(:hover) {
+ border-image-source: var(--button-rss);
+ }
+
+ &::after {
+ background: url("/images/rss.svg") center / 65% auto no-repeat;
+ content: "";
+ display: block;
+ inset: -15px;
+ position: absolute;
+ pointer-events: none;
+ }
+}
diff --git a/src/lib/escape.ts b/src/lib/escape.ts
new file mode 100644
index 0000000..fbdd55f
--- /dev/null
+++ b/src/lib/escape.ts
@@ -0,0 +1,29 @@
+const escapeEntities = new Map([
+ ["&", "&"],
+ ["<", "<"],
+ [">", ">"],
+ ['"', """],
+ ["'", "'"],
+]);
+
+const unescapeEntities = new Map([
+ ["&", "&"],
+ ["<", "<"],
+ [">", ">"],
+ [""", '"'],
+ ["'", "'"],
+]);
+
+const escapeKeys = new RegExp([...escapeEntities.keys()].join("|"), "g");
+const unescapeKeys = new RegExp([...unescapeEntities.keys()].join("|"), "g");
+
+/** Escape HTML entities */
+export const escape = (str: string): string =>
+ str.replaceAll(escapeKeys, (k) => escapeEntities.get(k)!);
+
+/** Unescape HTML entities */
+export const unescape = (str: string): string =>
+ str.replaceAll(unescapeKeys, (k) => unescapeEntities.get(k)!);
+
+/** Unescape and escape HTML entities */
+export const reEscape = (str: string) => escape(unescape(str));
diff --git a/src/routes/bookmarks/[did=did]/+page.svelte b/src/routes/bookmarks/[did=did]/+page.svelte
index 75d41ed..94a779c 100644
--- a/src/routes/bookmarks/[did=did]/+page.svelte
+++ b/src/routes/bookmarks/[did=did]/+page.svelte
@@ -1,4 +1,5 @@