Browse Source

speed up app

imwald
Silberengel 1 week ago
parent
commit
8a77edbcea
  1. 2
      assets/app.js
  2. 2
      assets/controllers/nostr_preview_controller.js
  3. 10
      assets/styles/a2hs.css
  4. 76
      assets/styles/app.css
  5. 70
      assets/styles/components/_nostr_previews.scss
  6. 98
      assets/styles/event.css
  7. 18
      assets/styles/layout.css
  8. 112
      assets/styles/nostr-previews.css
  9. 34
      assets/styles/og.css
  10. 12
      assets/styles/theme.css
  11. 6
      assets/theme/default/theme.css
  12. 18
      src/Twig/Components/Organisms/Comments.php
  13. 10
      templates/components/Molecules/NostrPreview.html.twig
  14. 45
      templates/components/Molecules/NostrPreviewContent.html.twig
  15. 2
      templates/components/Molecules/OgPreview.html.twig
  16. 85
      templates/event/index.html.twig

2
assets/app.js

@ -8,10 +8,12 @@ import './bootstrap.js'; @@ -8,10 +8,12 @@ import './bootstrap.js';
import './styles/fonts.css';
import './styles/theme.css';
import './styles/app.css';
import './styles/nostr-previews.css';
import './styles/layout.css';
import './styles/button.css';
import './styles/card.css';
import './styles/article.css';
import './styles/event.css';
import './styles/og.css';
import './styles/form.css';
import './styles/notice.css';

2
assets/controllers/nostr_preview_controller.js

@ -16,7 +16,7 @@ export default class extends Controller { @@ -16,7 +16,7 @@ export default class extends Controller {
async fetchPreview() {
try {
this.containerTarget.innerHTML = '<div class="text-center my-2"><div class="spinner-border spinner-border-sm text-secondary" role="status"></div> Loading preview...</div>';
this.containerTarget.innerHTML = '<div class="nostr-preview__loading text-center my-2"><span class="nostr-preview__spinner" role="status" aria-label="Loading"></span><span class="nostr-preview__loading-text ms-2">Loading preview…</span></div>';
if (this.typeValue === 'url' && this.fullMatchValue) {
// Fetch OG preview for plain URLs
fetch("/og-preview/", {

10
assets/styles/a2hs.css

@ -2,11 +2,19 @@ @@ -2,11 +2,19 @@
position: fixed;
bottom: 20px;
left: 20px;
background: #ffffff;
background: var(--color-bg);
color: var(--color-text);
border: 1px solid var(--color-border);
padding: 1rem;
border-radius: 8px;
z-index: 1000;
box-shadow: 0 4px 14px var(--color-shadow);
}
.install-prompt-box a:focus-visible,
.install-prompt-box button:focus-visible {
outline: 2px solid var(--color-focus-ring);
outline-offset: 2px;
}
.install-prompt-box.hidden {

76
assets/styles/app.css

@ -647,6 +647,82 @@ label.search { @@ -647,6 +647,82 @@ label.search {
margin-bottom: 15px;
}
/* Shared layout / theme utilities (replaces Bootstrap-only classes where removed) */
.text-subtle {
color: var(--color-text-mid);
}
.text-muted {
color: var(--color-text-mid);
}
.text-center {
text-align: center;
}
.my-2 {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.mt-2 {
margin-top: 0.5rem;
}
.ms-2 {
margin-left: 0.5rem;
}
.nostr-card-header {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 0.5rem;
}
.ui-badge {
display: inline-block;
padding: 0.2rem 0.55rem;
font-size: 0.75rem;
font-weight: 600;
line-height: 1.2;
border-radius: 4px;
letter-spacing: 0.02em;
}
.ui-badge--brand {
background-color: var(--brand-color);
color: var(--color-text-contrast);
}
.ui-badge--primary {
background-color: var(--color-primary);
color: var(--color-text-contrast);
}
.ui-badge--secondary {
background-color: var(--color-secondary);
color: var(--color-text-contrast);
}
.ui-badge--neutral {
background-color: var(--color-bg-light);
color: var(--color-text);
border: 1px solid var(--color-border);
}
.alert.alert-warning {
background-color: var(--color-bg-light);
color: var(--color-text);
border: 1px solid var(--color-border);
}
a:focus-visible {
outline: 2px solid var(--color-focus-ring);
outline-offset: 2px;
}
@media (max-width: 600px) {
h1.brand {
font-size: 2.2rem;

70
assets/styles/components/_nostr_previews.scss

@ -1,26 +1,71 @@ @@ -1,26 +1,71 @@
.nostr-preview__loading {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 0.25rem;
}
.nostr-preview__spinner {
box-sizing: border-box;
width: 1.25rem;
height: 1.25rem;
border: 2px solid var(--color-border);
border-top-color: var(--color-primary);
border-radius: 50%;
animation: nostr-preview-spin 0.75s linear infinite;
}
@keyframes nostr-preview-spin {
to {
transform: rotate(360deg);
}
}
.nostr-preview__loading-text {
color: var(--color-text-mid);
font-size: 0.9rem;
}
.nostr-preview {
margin-top: 0.5rem;
.nostr-event-preview, .nostr-profile-preview {
border-left: 3px solid #6c5ce7;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
.nostr-event-preview,
.nostr-profile-preview,
.nostr-address-preview,
.nostr-highlight-preview {
border-left: 3px solid var(--color-primary);
box-shadow: 0 2px 6px var(--color-shadow);
}
.nostr-profile-preview {
border-left-color: #00b894;
border-left-color: var(--color-secondary);
}
.nostr-highlight-preview {
border-left-color: var(--brand-color);
}
.card-title {
margin-bottom: 0.5rem;
font-size: 1rem;
color: var(--color-text);
}
.card-text {
font-size: 0.9rem;
color: var(--color-text);
}
.card-footer {
padding: 0.5rem 1rem;
color: var(--color-text-mid);
}
.nostr-profile-preview__body {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
}
@ -28,25 +73,32 @@ @@ -28,25 +73,32 @@
h6 {
font-size: 0.9rem;
margin-bottom: 1rem;
color: var(--color-text);
}
.preview-container {
// For multiple previews
max-height: 500px;
overflow-y: auto;
padding-right: 10px;
}
}
// Style for nostr links in text
/* In-content nostr: / npub links (CommonMark) */
.nostr-link {
color: #6c5ce7;
background-color: rgba(108, 92, 231, 0.1);
color: var(--color-link);
background-color: color-mix(in srgb, var(--color-primary) 18%, transparent);
padding: 0 3px;
border-radius: 3px;
text-decoration: none;
font-weight: 500;
&:hover {
background-color: rgba(108, 92, 231, 0.2);
background-color: color-mix(in srgb, var(--color-primary) 24%, transparent);
color: var(--color-link-hover);
}
&:focus-visible {
outline: 2px solid var(--color-focus-ring);
outline-offset: 2px;
}
}

98
assets/styles/event.css

@ -0,0 +1,98 @@ @@ -0,0 +1,98 @@
/* Nostr event detail page (EventController) */
.event-page {
max-width: 800px;
margin: 2rem auto;
padding: 1.5rem;
background-color: var(--color-bg);
color: var(--color-text);
border: 1px solid var(--color-border);
border-radius: 8px;
box-shadow: 0 2px 8px var(--color-shadow);
}
.event-page__header {
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
gap: 1rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--color-border);
padding-bottom: 1rem;
}
.event-page__content {
font-size: 1.1rem;
line-height: 1.6;
margin-bottom: 2rem;
white-space: pre-wrap;
color: var(--color-text);
}
.event-page__links {
margin: 1.5rem 0;
padding: 1rem;
background-color: var(--color-bg-light);
color: var(--color-text);
border: 1px solid var(--color-border);
border-radius: 6px;
}
.event-page__links h4 {
margin: 0 0 0.75rem;
font-size: 1.05rem;
font-weight: 600;
color: var(--color-text);
}
.event-page__links .link-list {
list-style: none;
padding-left: 0;
}
.event-page__links .link-list li {
margin-bottom: 0.5rem;
word-break: break-all;
}
.event-page__link-type {
color: var(--color-text-mid);
font-size: 0.9rem;
margin-left: 0.5rem;
}
.event-page__footer {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 1rem;
margin-top: 1.5rem;
padding-top: 1rem;
border-top: 1px solid var(--color-border);
}
.event-page__tags {
flex: 1;
color: var(--color-text);
}
.event-page__tags ul,
.event-page__references ul {
list-style-type: none;
padding-left: 0;
}
.event-page__tags li,
.event-page__references li {
margin-bottom: 0.5rem;
}
.event-page__meta {
color: var(--color-text-mid);
font-size: 0.95rem;
}
.event-page a:focus-visible {
outline: 2px solid var(--color-focus-ring);
outline-offset: 2px;
}

18
assets/styles/layout.css

@ -170,12 +170,13 @@ dt { @@ -170,12 +170,13 @@ dt {
/* Footer */
footer {
background-color: #333;
color: white;
background-color: var(--color-footer-bg);
color: var(--color-footer-text);
text-align: center;
padding: 1em 0;
position: relative;
width: 100%;
border-top: 1px solid var(--color-border);
}
footer .footer-links {
@ -183,7 +184,18 @@ footer .footer-links { @@ -183,7 +184,18 @@ footer .footer-links {
}
.footer-links a {
color: var(--color-text-light);
color: var(--color-footer-link);
text-decoration: underline;
text-underline-offset: 2px;
}
.footer-links a:hover {
color: var(--color-text);
}
.footer-links a:focus-visible {
outline: 2px solid var(--color-focus-ring);
outline-offset: 3px;
}
.search input {

112
assets/styles/nostr-previews.css

@ -0,0 +1,112 @@ @@ -0,0 +1,112 @@
/* Nostr link previews + in-content nostr: links (mirrors components/_nostr_previews.scss; loaded by app.js) */
.nostr-preview__loading {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 0.25rem;
}
.nostr-preview__spinner {
box-sizing: border-box;
width: 1.25rem;
height: 1.25rem;
border: 2px solid var(--color-border);
border-top-color: var(--color-primary);
border-radius: 50%;
animation: nostr-preview-spin 0.75s linear infinite;
}
@keyframes nostr-preview-spin {
to {
transform: rotate(360deg);
}
}
.nostr-preview__loading-text {
color: var(--color-text-mid);
font-size: 0.9rem;
}
.nostr-preview {
margin-top: 0.5rem;
}
.nostr-preview .nostr-preview-link a {
color: var(--color-link);
text-decoration: underline;
text-underline-offset: 2px;
}
.nostr-preview .nostr-preview-link a:hover {
color: var(--color-link-hover);
}
.nostr-preview .nostr-event-preview,
.nostr-preview .nostr-profile-preview,
.nostr-preview .nostr-address-preview,
.nostr-preview .nostr-highlight-preview {
border-left: 3px solid var(--color-primary);
box-shadow: 0 2px 6px var(--color-shadow);
}
.nostr-preview .nostr-profile-preview {
border-left-color: var(--color-secondary);
}
.nostr-preview .nostr-highlight-preview {
border-left-color: var(--brand-color);
}
.nostr-preview .card-title {
margin-bottom: 0.5rem;
font-size: 1rem;
color: var(--color-text);
}
.nostr-preview .card-text {
font-size: 0.9rem;
color: var(--color-text);
}
.nostr-preview .card-footer {
padding: 0.5rem 1rem;
color: var(--color-text-mid);
}
.nostr-preview .nostr-profile-preview__body {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.nostr-previews h6 {
font-size: 0.9rem;
margin-bottom: 1rem;
color: var(--color-text);
}
.nostr-previews .preview-container {
max-height: 500px;
overflow-y: auto;
padding-right: 10px;
}
.nostr-link {
color: var(--color-link);
background-color: color-mix(in srgb, var(--color-primary) 18%, transparent);
padding: 0 3px;
border-radius: 3px;
text-decoration: none;
font-weight: 500;
}
.nostr-link:hover {
background-color: color-mix(in srgb, var(--color-primary) 24%, transparent);
color: var(--color-link-hover);
}
.nostr-link:focus-visible {
outline: 2px solid var(--color-focus-ring);
outline-offset: 2px;
}

34
assets/styles/og.css

@ -3,9 +3,43 @@ @@ -3,9 +3,43 @@
padding: 20px;
margin: 10px 0;
background-color: var(--color-bg);
color: var(--color-text);
border: 1px solid var(--color-border);
border-radius: 6px;
}
.og-preview-image {
width: 100%;
height: auto;
}
.og-preview-content a {
color: var(--color-link);
text-decoration: underline;
text-underline-offset: 2px;
}
.og-preview-content a:hover {
color: var(--color-link-hover);
}
.og-preview-content a:focus-visible {
outline: 2px solid var(--color-focus-ring);
outline-offset: 2px;
}
.og-preview-title a {
color: var(--color-text);
text-decoration: none;
font-weight: 700;
}
.og-preview-title a:hover {
color: var(--color-primary);
text-decoration: underline;
}
.og-preview-description {
color: var(--color-text-mid);
margin-top: 0.35rem;
}

12
assets/styles/theme.css

@ -9,15 +9,25 @@ @@ -9,15 +9,25 @@
--color-bg-light: #2a2a2a; /* Slightly lighter charcoal */
--color-bg-primary: #2e1f2e; /* Muted aubergine for a rich, elegant feel */
--color-text: #f5f5f5; /* Soft white for readability */
--color-text-mid: #d8d8d8; /* Warm light gray */
--color-text-mid: #d8d8d8; /* Warm light gray — use on dark bg for ≥4.5:1 vs --color-bg */
--color-text-contrast: #000; /* Black text for contrast */
--color-primary: #5F7355; /* Plum primary color */
--color-secondary: #495544; /* secondary color */
--color-border: #3a3a3a; /* Subtle gray border */
/* Aliases / derived (WCAG AA on typical surfaces when paired as documented) */
--color-text-light: var(--color-text-mid); /* deprecated name: use --color-text-mid */
--color-footer-bg: var(--color-bg-light);
--color-footer-text: var(--color-text);
--color-footer-link: var(--color-primary);
--color-link: var(--color-secondary);
--color-link-hover: var(--color-text);
--color-focus-ring: var(--color-secondary);
--color-shadow: color-mix(in srgb, var(--color-text) 16%, transparent);
--font-family: 'Montserrat', serif; /* Set the Montserrat font as default */
--main-body-font: 'Newsreader', serif; /* Set the font for the main body */
--heading-font: 'EB Garamond', serif; /* Set the font for headings */
--brand-font: 'Lobster', serif; /* A classic, refined branding font */
--brand-color: white;
--accent-color: var(--color-secondary);
}

6
assets/theme/default/theme.css

@ -2,7 +2,11 @@ @@ -2,7 +2,11 @@
--color-bg: #f4f1ee;
--color-bg-light: #e8e4df;
--color-text: #2a2a2a;
--color-text-mid: #3a3a3a; /* Warm light gray */
--color-text-mid: #3d3a36;
--color-text-contrast: #f4f1ee;
--brand-color: black;
--color-link: #3a342f;
--color-link-hover: #1a1a1a;
--color-focus-ring: #1a1a1a;
--color-shadow: color-mix(in srgb, var(--color-text) 10%, transparent);
}

18
src/Twig/Components/Organisms/Comments.php

@ -4,19 +4,23 @@ namespace App\Twig\Components\Organisms; @@ -4,19 +4,23 @@ namespace App\Twig\Components\Organisms;
use App\Service\NostrClient;
use App\Service\NostrLinkParser;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
#[AsTwigComponent]
final class Comments
{
public array $list = [];
public array $commentLinks = [];
public array $processedContent = [];
public function __construct(
private readonly NostrClient $nostrClient,
private readonly NostrLinkParser $nostrLinkParser
private readonly NostrLinkParser $nostrLinkParser,
private readonly CacheInterface $cache,
) {
}
@ -25,10 +29,14 @@ final class Comments @@ -25,10 +29,14 @@ final class Comments
*/
public function mount($current): void
{
// Fetch comments
$this->list = $this->nostrClient->getComments($current);
$cacheKey = 'comments_' . hash('sha256', (string) $current);
$this->list = $this->cache->get($cacheKey, function (ItemInterface $item) use ($current) {
$item->expiresAfter(120);
return $this->nostrClient->getComments($current);
});
// Parse Nostr links in comments but don't fetch previews
$this->parseNostrLinks();
}

10
templates/components/Molecules/NostrPreview.html.twig

@ -5,9 +5,9 @@ @@ -5,9 +5,9 @@
data-nostr-preview-decoded-value="{{ preview.data|json_encode }}"
data-nostr-preview-full-match-value="{{ preview.full_match }}">
<div data-nostr-preview-target="container">
<div class="text-center my-2">
<div class="spinner-border spinner-border-sm text-secondary" role="status"></div>
<span class="ms-2">Loading preview...</span>
<div class="nostr-preview__loading text-center my-2">
<span class="nostr-preview__spinner" role="status" aria-label="Loading"></span>
<span class="nostr-preview__loading-text ms-2">Loading preview…</span>
</div>
{% if preview.full_match is defined and preview.full_match %}
<div class="nostr-preview-link mt-2">
@ -16,11 +16,9 @@ @@ -16,11 +16,9 @@
{% endif %}
{% if preview.data is not null %}
<div class="nostr-preview-details mt-2">
{# Example: show kind if available #}
{% if preview.data.kind is defined %}
<span class="badge bg-info">Kind: {{ preview.data.kind }}</span>
<span class="ui-badge ui-badge--neutral">Kind: {{ preview.data.kind }}</span>
{% endif %}
{# Add more event details here as needed #}
</div>
{% endif %}
</div>

45
templates/components/Molecules/NostrPreviewContent.html.twig

@ -12,14 +12,13 @@ @@ -12,14 +12,13 @@
{% endfor %}
</div>
{% elseif preview.type == 'nevent' %}
{# If kind is 9802 - Highlight #}
{% if preview.kind == 9802 %}
<div class="card nostr-highlight-preview">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-header nostr-card-header">
<div>
<twig:Molecules:UserFromNpub ident="{{ preview.pubkey }}" />
</div>
<span class="badge bg-warning">Highlight</span>
<span class="ui-badge ui-badge--brand">Highlight</span>
</div>
<div class="card-body">
<p>{{ preview.content }}</p>
@ -40,41 +39,53 @@ @@ -40,41 +39,53 @@
</div>
</div>
{% else %}
{% set is_longform = preview.kind == 30023 or preview.kind == 30024 %}
{% set article_title = null %}
{% set article_summary = null %}
{% if preview.tags is defined %}
{% for tag in preview.tags %}
{% if tag[0] == 'title' %}
{% set article_title = tag[1] %}
{% elseif tag[0] == 'summary' %}
{% set article_summary = tag[1] %}
{% endif %}
{% endfor %}
{% endif %}
<div class="card nostr-event-preview">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-header nostr-card-header">
<div>
<twig:Molecules:UserFromNpub ident="{{ preview.author }}" />
<twig:Molecules:UserFromNpub ident="{{ preview.pubkey }}" />
</div>
{% if preview.type == 'event' %}
<span class="badge bg-secondary">Article</span>
{% if is_longform %}
<span class="ui-badge ui-badge--secondary">Article</span>
{% else %}
<span class="badge bg-info">Note</span>
<span class="ui-badge ui-badge--primary">Note</span>
{% endif %}
</div>
<div class="card-body">
{% if preview.type == 'event' %}
<h5 class="card-title">{{ preview.title }}</h5>
{% if is_longform %}
<h5 class="card-title">{{ article_title ?: 'Article' }}</h5>
<p class="card-text">
{% if preview.event.summary is defined %}
{{ preview.event.summary }}
{% if article_summary %}
{{ article_summary }}
{% else %}
{{ preview.event.content|length > 150 ? preview.event.content|slice(0, 150) ~ '...' : preview.event.content }}
{{ preview.content|length > 150 ? preview.content|slice(0, 150) ~ '...' : preview.content }}
{% endif %}
</p>
{% else %}
<p class="card-text">{{ preview.content|length > 280 ? preview.content|slice(0, 280) ~ '...' : preview.content }}</p>
{% endif %}
</div>
<div class="card-footer text-muted">
<small>{{ preview.event.created_at is defined ? preview.event.created_at|date('F j Y') : '' }}</small>
<div class="card-footer nostr-preview-card__meta text-subtle">
<small>{{ preview.created_at is defined ? preview.created_at|date('F j Y') : '' }}</small>
</div>
</div>
{% endif %}
{% elseif preview.type == 'nprofile' %}
<div class="card nostr-profile-preview">
<div class="card-body d-flex">
<div class="card-body nostr-profile-preview__body">
<h5 class="card-title">{{ preview.display_name ?: preview.name }} </h5>
<small class="text-muted">@{{ preview.npub|shortenNpub }}</small>
<small class="text-subtle">@{{ preview.npub|shortenNpub }}</small>
{% if preview.about %}
<p class="card-text">{{ preview.about|length > 150 ? preview.about|slice(0, 150) ~ '...' : preview.about }}</p>
{% endif %}

2
templates/components/Molecules/OgPreview.html.twig

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
<a href="{{ og.url }}" target="_blank" rel="noopener noreferrer"><b>{{ og.title ?: og.url }}</b></a>
</div>
{% if og.description %}
<div class="og-preview-description text-muted">{{ og.description }}</div>
<div class="og-preview-description">{{ og.description|e }}</div>
{% endif %}
</div>
</div>

85
templates/event/index.html.twig

@ -4,8 +4,8 @@ @@ -4,8 +4,8 @@
{% block body %}
<div class="container">
<div class="event-container">
<div class="event-header">
<div class="event-page">
<div class="event-page__header">
{% if author %}
{% set author_pic = null %}
{% if author.picture is defined and author.picture %}
@ -27,30 +27,30 @@ @@ -27,30 +27,30 @@
</div>
<hr />
{% endif %}
<div class="event-meta">
<div class="event-page__meta">
<span class="event-date">{{ event.created_at|date('F j, Y - H:i') }}</span>
</div>
</div>
<div class="event-content">
<div class="event-page__content">
<twig:Atoms:Content :content="event.content" />
</div>
{% if nostrLinks is defined and nostrLinks|length > 0 %}
<div class="nostr-links">
<div class="event-page__links">
<h4>Referenced Nostr Links</h4>
<ul class="link-list">
{% for link in nostrLinks %}
<li>
<a href="/e/{{ link.identifier }}">{{ link.identifier }}</a>
<span class="link-type">({{ link.type }})</span>
<span class="event-page__link-type">({{ link.type }})</span>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="event-footer">
<div class="event-tags">
<div class="event-page__footer">
<div class="event-page__tags">
{% if event.tags is defined and event.tags|length > 0 %}
<ul>
{% for tag in event.tags %}
@ -74,73 +74,4 @@ @@ -74,73 +74,4 @@
{% block stylesheets %}
{{ parent() }}
<style>
.event-container {
max-width: 800px;
margin: 2rem auto;
padding: 1.5rem;
background: #fff;
border-radius: 8px;
}
.event-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1.5rem;
border-bottom: 1px solid #eee;
padding-bottom: 1rem;
}
.event-content {
font-size: 1.1rem;
line-height: 1.6;
margin-bottom: 2rem;
white-space: pre-wrap;
}
.nostr-links {
margin: 1.5rem 0;
padding: 1rem;
background-color: #f9f9f9;
border-radius: 4px;
}
.link-list {
list-style: none;
padding-left: 0;
}
.link-list li {
margin-bottom: 0.5rem;
word-break: break-all;
}
.link-type {
color: #6c757d;
font-size: 0.9rem;
margin-left: 0.5rem;
}
.event-footer {
display: flex;
justify-content: space-between;
margin-top: 1.5rem;
padding-top: 1rem;
border-top: 1px solid #eee;
}
.event-tags {
flex: 1;
}
.event-tags ul, .event-references ul {
list-style-type: none;
padding-left: 0;
}
.event-tags li, .event-references li {
margin-bottom: 0.5rem;
}
</style>
{% endblock %}

Loading…
Cancel
Save