feat: restyle episode and person cards + add focus style to interactive elements for a11y

fix components in follow and remote action pages by calling new instances directly
This commit is contained in:
Yassine Doghri 2021-10-21 13:12:38 +00:00
parent 025b2f42e6
commit a505a1de56
44 changed files with 250 additions and 248 deletions

View File

@ -238,7 +238,7 @@ if (! function_exists('location_link')) {
icon('map-pin', 'mr-2') . $location->name,
[
'class' =>
'inline-flex items-baseline hover:underline' .
'inline-flex items-baseline hover:underline focus:ring-castopod' .
($class === '' ? '' : " {$class}"),
'target' => '_blank',
'rel' => 'noreferrer noopener',

View File

@ -20,17 +20,17 @@ if (! function_exists('render_page_links')) {
{
$pages = (new PageModel())->findAll();
$links = anchor(route_to('home'), lang('Common.home'), [
'class' => 'px-2 py-1 underline hover:no-underline',
'class' => 'px-2 py-1 underline hover:no-underline focus:ring-castopod',
]);
$links .= anchor(route_to('credits'), lang('Person.credits'), [
'class' => 'px-2 py-1 underline hover:no-underline',
'class' => 'px-2 py-1 underline hover:no-underline focus:ring-castopod',
]);
$links .= anchor(route_to('map'), lang('Page.map'), [
'class' => 'px-2 py-1 underline hover:no-underline',
'class' => 'px-2 py-1 underline hover:no-underline focus:ring-castopod',
]);
foreach ($pages as $page) {
$links .= anchor($page->link, $page->title, [
'class' => 'px-2 py-1 underline hover:no-underline',
'class' => 'px-2 py-1 underline hover:no-underline focus:ring-castopod',
]);
}

View File

@ -18,6 +18,7 @@ return [
'no_episode_hint' =>
'Navigate the podcast episodes with the navigation bar above.',
'follow' => 'Follow',
'followTitle' => 'Follow {actorDisplayName} on the fediverse!',
'followers' => '{numberOfFollowers, plural,
one {<span class="font-semibold">#</span> follower}
other {<span class="font-semibold">#</span> followers}

View File

@ -18,6 +18,7 @@ return [
'no_episode_hint' =>
'Naviguez au sein des épisodes du podcast episodes grâce à la barre de navigation ci-dessus.',
'follow' => 'Suivre',
'followTitle' => 'Suivez {actorDisplayName} sur le fédiverse!',
'followers' => '{numberOfFollowers, plural,
one {<span class="font-semibold">#</span> abonné·e}
other {<span class="font-semibold">#</span> abonné·e·s}

View File

@ -25,9 +25,9 @@ const Dropdown = (): void => {
const offsetY = menu.dataset.dropdownOffsetY
? parseInt(menu.dataset.dropdownOffsetY)
: 0;
console.log(offsetX, offsetY);
popperInstance = createPopper(button, menu, {
placement: menu.dataset.dropdownPlacement as Placement,
// strategy: "fixed",
modifiers: [
{
name: "offset",

View File

@ -17,4 +17,19 @@
.rounded-conditional-2xl {
border-radius: max(0px, min(1rem, calc((100vw - 1rem - 100%) * 9999)));
}
.backdrop-gradient {
background-image: linear-gradient(
180deg,
hsla(0, 0%, 35.29%, 0) 0%,
hsla(0, 0%, 34.53%, 0.034375) 16.36%,
hsla(0, 0%, 32.42%, 0.125) 33.34%,
hsla(0, 0%, 29.18%, 0.253125) 50.1%,
hsla(0, 0%, 24.96%, 0.4) 65.75%,
hsla(0, 0%, 19.85%, 0.546875) 79.43%,
hsla(0, 0%, 13.95%, 0.675) 90.28%,
hsla(0, 0%, 7.32%, 0.765625) 97.43%,
hsla(0, 0%, 0%, 0.8) 100%
);
}
}

View File

@ -76,11 +76,17 @@ class Button extends Component
}
if ($this->iconLeft !== '') {
$this->slot = '<Icon glyph="' . $this->iconLeft . '" class="mr-2 opacity-75" />' . $this->slot;
$this->slot = (new Icon([
'glyph' => $this->iconLeft,
'class' => 'mr-2 opacity-75',
]))->render() . $this->slot;
}
if ($this->iconRight !== '') {
$this->slot .= '<Icon glyph="' . $this->iconRight . '" class="ml-2 opacity-75" />';
$this->slot .= (new Icon([
'glyph' => $this->iconRight,
'class' => 'ml-2 opacity-75',
]))->render();
}
unset($this->attributes['slot']);

View File

@ -11,6 +11,12 @@ class DropdownMenu extends Component
{
public string $id = '';
public string $placement = 'bottom-end';
public string $offsetX = '0';
public string $offsetY = '0';
public array $items = [];
public function setItems(string $value): void
@ -48,7 +54,9 @@ class DropdownMenu extends Component
class="absolute z-50 flex flex-col py-2 text-black whitespace-no-wrap bg-white border-black rounded-lg border-3"
aria-labelledby="{$this->labelledby}"
data-dropdown="menu"
data-dropdown-placement="bottom-end">{$menuItems}</nav>
data-dropdown-placement="{$this->placement}"
data-dropdown-offset-x="{$this->offsetX}"
data-dropdown-offset-y="{$this->offsetY}">{$menuItems}</nav>
HTML;
}
}

View File

@ -17,12 +17,16 @@ class Checkbox extends FormComponent
public function render(): string
{
$attributes = [
'id' => $this->value,
'name' => $this->name,
'class' => 'form-checkbox text-pine-500 border-black border-3 focus:ring-castopod w-6 h-6',
];
if ($this->required) {
$attributes['required'] = 'required';
}
$checkboxInput = form_checkbox(
[
'id' => $this->value,
'name' => $this->name,
'class' => 'form-checkbox text-pine-500 border-black border-3 focus:ring-castopod w-6 h-6',
],
$attributes,
'yes',
old($this->name) ? old($this->name) === $this->value : $this->isChecked,
);
@ -30,10 +34,7 @@ class Checkbox extends FormComponent
$hint = $this->hint === null ? '' : hint_tooltip($this->hint, 'ml-1');
return <<<HTML
<label class="leading-8 {$this->class}">
{$checkboxInput}
<span class="ml-2">{$this->slot}{$hint}</label>
</label>
<label class="inline-flex items-center {$this->class}">{$checkboxInput}<span class="ml-2">{$this->slot}{$hint}</span></label>
HTML;
}
}

View File

@ -40,12 +40,15 @@ class Field extends FormComponent
unset($fieldComponentAttributes['helperText']);
unset($fieldComponentAttributes['hintText']);
$fieldComponentAttributes = flatten_attributes($fieldComponentAttributes);
$fieldComponentAttributes['class'] = 'mb-1';
$element = __NAMESPACE__ . '\\' . $this->as;
$fieldElement = new $element($fieldComponentAttributes);
return <<<HTML
<div class="flex flex-col {$this->class}">
<Forms.Label {$labelAttributes}>{$this->label}</Forms.Label>
<Forms.{$this->as} {$fieldComponentAttributes} class="mb-1"/>
{$fieldElement->render()}
{$helperText}
</div>
HTML;

View File

@ -26,7 +26,7 @@ class Radio extends FormComponent
);
return <<<HTML
<label class="leading-8">{$radioInput}<span class="ml-2">{$this->slot}</span></label>
<label class="inline-flex items-center {$this->class}">{$radioInput}<span class="ml-2">{$this->slot}</span></label>
HTML;
}
}

View File

@ -23,7 +23,7 @@ class Heading extends Component
'large' => 'text-3xl',
];
$class = $this->class . ' relative z-10 font-bold text-pine-800 font-display before:w-full before:absolute before:h-1/2 before:left-0 before:bottom-0 before:rounded-full before:bg-pine-100 before:-z-10 ' . $sizeClasses[$this->size];
$class = $this->class . ' relative z-10 font-bold text-pine-800 font-display before:w-full before:absolute before:h-1/2 before:left-0 before:bottom-0 before:rounded-full before:bg-pine-100 before:z-[-10] ' . $sizeClasses[$this->size];
return <<<HTML
<{$this->tagName} class="{$class}">{$this->slot}</{$this->tagName}>

View File

@ -51,20 +51,15 @@ module.exports = {
admin: "300px calc(100% - 300px)",
podcast: "1fr minmax(auto, 960px) 1fr",
podcastMain: "1fr minmax(200px, 300px)",
podcasts: "repeat(auto-fill, minmax(14rem, 1fr))",
cards: "repeat(auto-fill, minmax(14rem, 1fr))",
latestEpisodes: "repeat(5, 1fr)",
},
gridTemplateRows: {
admin: "40px 1fr",
},
zIndex: {
"-10": "-10",
},
borderWidth: {
3: "3px",
},
ringWidth: {
3: "3px",
},
},
},
variants: {},

View File

@ -26,11 +26,11 @@
<div class="flex flex-col justify-end w-full -mt-4 sticky-header-inner">
<?= render_breadcrumb('text-gray-800 text-xs items-center flex') ?>
<div class="flex justify-between py-1">
<div class="flex flex-wrap items-center">
<Heading tagName="h1" size="large"><?= $this->renderSection('pageTitle') ?></Heading>
<div class="flex flex-wrap items-center overflow-x-hidden">
<Heading tagName="h1" size="large" class="truncate"><?= $this->renderSection('pageTitle') ?></Heading>
<?= $this->renderSection('headerLeft') ?>
</div>
<div class="flex gap-1"><?= $this->renderSection('headerRight') ?></div>
<div class="flex flex-shrink-0 gap-1"><?= $this->renderSection('headerRight') ?></div>
</div>
</div>
</header>

View File

@ -9,7 +9,7 @@
<footer class="px-2 py-2 mx-auto text-xs text-right">
<?= lang('Common.powered_by', [
'castopod' =>
'<a class="inline-flex font-semibold hover:underline" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a> ' .
'<a class="inline-flex font-semibold hover:underline focus:ring-castopod" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a> ' .
CP_VERSION,
]) ?>
</footer>

View File

@ -22,7 +22,7 @@
aria-expanded="false"><div class="relative mr-1">
<?= icon('account-circle', 'text-3xl opacity-60') ?>
<?= user()
->podcasts === [] ? '' : '<img src="' . interact_as_actor()->avatar_image_url . '" class="absolute bottom-0 w-4 h-4 rounded-full -right-1" />' ?>
->podcasts === [] ? '' : '<img src="' . interact_as_actor()->avatar_image_url . '" class="absolute bottom-0 w-4 h-4 border rounded-full -right-1 border-pine-800" />' ?>
</div>
<?= user()->username ?>
<?= icon('caret-down', 'ml-auto text-2xl') ?></button>

View File

@ -0,0 +1,50 @@
<article class="relative flex flex-col flex-1 flex-shrink-0 w-full transition group overflow-hidden bg-white border-2 snap-center hover:shadow-lg focus-within:shadow-lg focus-within:ring-castopod border-pine-100 rounded-xl min-w-[12rem] max-w-[17rem]">
<a href="<?= route_to('episode-view', $episode->podcast->id, $episode->id) ?>" class="flex flex-col justify-end w-full h-full text-white group">
<div class="absolute bottom-0 left-0 z-10 w-full h-full backdrop-gradient"></div>
<div class="w-full h-full overflow-hidden">
<img src="<?= $episode->image->medium_url ?>" alt="<?= $episode->title ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" />
</div>
<?= publication_pill($episode->published_at, $episode->publication_status, 'absolute top-0 left-0 ml-2 mt-2 text-sm'); ?>
<div class="absolute z-20 flex flex-col items-start px-4 py-2">
<?= episode_numbering($episode->number, $episode->season_number, 'text-xs font-semibold !no-underline border px-1 border-gray-500 mr-1', true) ?>
<span class="font-semibold leading-tight line-clamp-2"><?= $episode->title ?></span>
</div>
</a>
<button class="absolute top-0 right-0 z-10 p-2 mt-2 mr-2 text-white transition -translate-y-12 rounded-full opacity-0 focus:ring-castopod focus:opacity-100 focus:-translate-y-0 group-hover:translate-y-0 bg-black/50 group-hover:opacity-100" id="more-dropdown-<?= $episode->id ?>" data-dropdown="button" data-dropdown-target="more-dropdown-<?= $episode->id ?>-menu" aria-haspopup="true" aria-expanded="false" title="<?= lang('Common.more') ?>"><?= icon('more') ?></button>
<DropdownMenu id="more-dropdown-<?= $episode->id ?>-menu" labelledby="more-dropdown-<?= $episode->id ?>" offsetY="-32" items="<?= esc(json_encode([
[
'type' => 'link',
'title' => lang('Episode.go_to_page'),
'uri' => route_to('episode', $episode->podcast->handle, $episode->slug),
],
[
'type' => 'link',
'title' => lang('Episode.edit'),
'uri' => route_to('episode-edit', $episode->podcast->id, $episode->id),
],
[
'type' => 'link',
'title' => lang('Episode.embed.title'),
'uri' => route_to('embed-add', $episode->podcast->id, $episode->id),
],
[
'type' => 'link',
'title' => lang('Person.persons'),
'uri' => route_to('episode-persons-manage', $episode->podcast->id, $episode->id),
],
[
'type' => 'link',
'title' => lang('Episode.soundbites'),
'uri' => route_to('soundbites-edit', $episode->podcast->id, $episode->id),
],
[
'type' => 'separator',
],
[
'type' => 'link',
'title' => lang('Episode.delete'),
'uri' => route_to('episode-delete', $episode->podcast->id, $episode->id),
'class' => 'font-semibold text-red-600',
],
])) ?>" />
</article>

View File

@ -11,7 +11,7 @@
<?= $this->section('content') ?>
<Alert variant="danger" glyph="alert"><?= lang('Episode.form.warning') ?></Alert>
<Alert variant="danger" glyph="alert" class="max-w-xl"><?= lang('Episode.form.warning') ?></Alert>
<form action="<?= route_to('episode-create', $podcast->id) ?>" method="POST" enctype="multipart/form-data" class="flex flex-col mt-6 gap-y-8">
<?= csrf_field() ?>

View File

@ -15,7 +15,7 @@
<?= $this->section('content') ?>
<Alert variant="danger" glyph="alert"><?= lang('Episode.form.warning') ?></Alert>
<Alert variant="danger" glyph="alert" class="max-w-xl"><?= lang('Episode.form.warning') ?></Alert>
<form id="episode-edit-form" action="<?= route_to('episode-edit', $podcast->id, $episode->id) ?>" method="POST" enctype="multipart/form-data" class="flex flex-col mt-6 gap-y-8">
<?= csrf_field() ?>

View File

@ -73,10 +73,15 @@
[
'header' => lang('Episode.list.actions'),
'cell' => function ($episode, $podcast) {
return '<button id="more-dropdown-' . $episode->id . '" type="button" class="inline-flex items-center p-1 focus:ring-castopod" data-dropdown="button" data-dropdown-target="more-dropdown-' . $episode->id . '-menu" aria-haspopup="true" aria-expanded="false">' .
return '<button id="more-dropdown-' . $episode->id . '" type="button" class="inline-flex items-center p-1 rounded-full focus:ring-castopod" data-dropdown="button" data-dropdown-target="more-dropdown-' . $episode->id . '-menu" aria-haspopup="true" aria-expanded="false">' .
icon('more') .
'</button>' .
'<DropdownMenu id="more-dropdown-' . $episode->id . '-menu" labelledby="more-dropdown-' . $episode->id . '" items="' . esc(json_encode([
'<DropdownMenu id="more-dropdown-' . $episode->id . '-menu" labelledby="more-dropdown-' . $episode->id . '" offsetY="-24" items="' . esc(json_encode([
[
'type' => 'link',
'title' => lang('Episode.go_to_page'),
'uri' => route_to('episode', $podcast->handle, $episode->slug),
],
[
'type' => 'link',
'title' => lang('Episode.edit'),
@ -97,11 +102,6 @@
'title' => lang('Episode.soundbites'),
'uri' => route_to('soundbites-edit', $podcast->id, $episode->id),
],
[
'type' => 'link',
'title' => lang('Episode.go_to_page'),
'uri' => route_to('episode', $podcast->handle, $episode->slug),
],
[
'type' => 'separator',
],

View File

@ -14,7 +14,7 @@
route_to('episode-view', $podcast->id, $episode->id),
icon('arrow-left', 'mr-2 text-lg') . lang('Episode.publish_form.back_to_episode_dashboard'),
[
'class' => 'inline-flex items-center font-semibold mr-4 text-sm',
'class' => 'inline-flex items-center font-semibold mr-4 text-sm focus:ring-castopod',
],
) ?>

View File

@ -0,0 +1,33 @@
<article class="relative h-full overflow-hidden transition bg-white shadow border-3 border-pine-100 rounded-xl group hover:shadow-xl focus-within:shadow-xl focus-within:ring-castopod">
<a href="<?= route_to('person-view', $person->id) ?>" class="flex flex-col justify-end w-full h-full text-white group">
<div class="absolute bottom-0 left-0 z-10 w-full h-full backdrop-gradient"></div>
<div class="w-full h-full overflow-hidden">
<img alt="<?= $person->full_name ?>" src="<?= $person->image->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" />
</div>
<div class="absolute z-20">
<h2 class="px-4 py-2 font-semibold leading-tight"><?= $person->full_name ?></h2>
</div>
</a>
<button class="absolute top-0 right-0 z-10 p-2 mt-2 mr-2 text-white transition -translate-y-12 rounded-full opacity-0 focus:ring-castopod focus:opacity-100 focus:-translate-y-0 group-hover:translate-y-0 bg-black/50 group-hover:opacity-100" id="more-dropdown-<?= $person->id ?>" data-dropdown="button" data-dropdown-target="more-dropdown-<?= $person->id ?>-menu" aria-haspopup="true" aria-expanded="false" title="<?= lang('Common.more') ?>"><?= icon('more') ?></button>
<DropdownMenu id="more-dropdown-<?= $person->id ?>-menu" labelledby="more-dropdown-<?= $person->id ?>" offsetY="-32" items="<?= esc(json_encode([
[
'type' => 'link',
'title' => lang('Person.view'),
'uri' => route_to('person-view', $person->id),
],
[
'type' => 'link',
'title' => lang('Person.edit'),
'uri' => route_to('person-edit', $person->id),
],
[
'type' => 'separator',
],
[
'type' => 'link',
'title' => lang('Person.delete'),
'uri' => route_to('person-delete', $person->id),
'class' => 'font-semibold text-red-600',
],
])) ?>" />
</article>

View File

@ -11,11 +11,9 @@
<?= $this->section('content') ?>
<form action="<?= route_to('person-edit', $person->id) ?>" method="POST" class="flex flex-col" enctype="multipart/form-data">
<form action="<?= route_to('person-edit', $person->id) ?>" method="POST" class="flex flex-col max-w-sm gap-y-4" enctype="multipart/form-data">
<?= csrf_field() ?>
<img src="<?= $person->image->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-32 h-32 mt-3 rounded" />
<Forms.Field
name="image"
label="<?= lang('Person.form.image') ?>"

View File

@ -14,47 +14,16 @@
<?= $this->section('content') ?>
<div class="flex flex-wrap">
<?php if ($persons !== null): ?>
<?php if ($persons !== null): ?>
<div class="grid gap-4 grid-cols-cards">
<?php foreach ($persons as $person): ?>
<article class="w-48 h-full mb-4 mr-4 overflow-hidden bg-white border rounded shadow">
<img
alt="<?= $person->full_name ?>"
src="<?= $person->image
->thumbnail_url ?>" class="object-cover w-full" />
<div class="p-2">
<a href="<?= route_to(
'person-view',
$person->id,
) ?>" class="hover:underline">
<h2 class="font-semibold"><?= $person->full_name ?></h2>
</a>
</div>
<footer class="flex items-center justify-end p-2">
<a class="inline-flex p-2 mr-2 text-teal-700 bg-teal-100 rounded-full shadow-xs hover:bg-teal-200" href="<?= route_to(
'person-edit',
$person->id,
) ?>" data-toggle="tooltip" data-placement="bottom" title="<?= lang(
'Person.edit',
) ?>"><?= icon('edit') ?></a>
<a class="inline-flex p-2 mr-2 text-gray-700 bg-red-100 rounded-full shadow-xs hover:bg-gray-200" href="<?= route_to(
'person-delete',
$person->id,
) ?>" data-toggle="tooltip" data-placement="bottom" title="<?= lang(
'Person.delete',
) ?>"><?= icon('delete-bin') ?></a>
<a class="inline-flex p-2 text-gray-700 bg-gray-100 rounded-full shadow-xs hover:bg-gray-200" href="<?= route_to(
'person-view',
$person->id,
) ?>" data-toggle="tooltip" data-placement="bottom" title="<?= lang(
'Person.view',
) ?>"><?= icon('eye') ?></a>
</footer>
</article>
<?= view('person/_card', [
'person' => $person,
]) ?>
<?php endforeach; ?>
<?php else: ?>
<p class="italic"><?= lang('Person.no_person') ?></p>
<?php endif; ?>
</div>
</div>
<?php else: ?>
<p class="italic"><?= lang('Person.no_person') ?></p>
<?php endif; ?>
<?= $this->endSection() ?>

View File

@ -0,0 +1,27 @@
<article class="relative h-full overflow-hidden transition bg-white shadow border-3 border-pine-100 group rounded-xl hover:shadow-xl focus-within:shadow-xl focus-within:ring-castopod">
<a href="<?= route_to('podcast-view', $podcast->id) ?>" class="flex flex-col justify-end w-full h-full text-white group">
<div class="absolute bottom-0 left-0 z-10 w-full h-full backdrop-gradient"></div>
<div class="w-full h-full overflow-hidden">
<img
alt="<?= $podcast->title ?>"
src="<?= $podcast->image->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" />
</div>
<div class="absolute z-20 px-4 pb-4 transition duration-75 ease-out translate-y-6 group-focus:translate-y-0 group-hover:translate-y-0">
<h2 class="font-bold leading-none truncate font-display"><?= $podcast->title ?></h2>
<p class="text-sm transition duration-150 opacity-0 group-focus:opacity-75 group-hover:opacity-75">@<?= $podcast->handle ?></p>
</div>
</a>
<button class="absolute top-0 right-0 z-10 p-2 mt-2 mr-2 text-white transition -translate-y-12 rounded-full opacity-0 focus:ring-castopod focus:opacity-100 focus:-translate-y-0 group-hover:translate-y-0 bg-black/50 group-hover:opacity-100" id="more-dropdown-<?= $podcast->id ?>" data-dropdown="button" data-dropdown-target="more-dropdown-<?= $podcast->id ?>-menu" aria-haspopup="true" aria-expanded="false" title="<?= lang('Common.more') ?>"><?= icon('more') ?></button>
<DropdownMenu id="more-dropdown-<?= $podcast->id ?>-menu" labelledby="more-dropdown-<?= $podcast->id ?>" offsetY="-32" items="<?= esc(json_encode([
[
'type' => 'link',
'title' => lang('Podcast.view'),
'uri' => route_to('podcast-view', $podcast->id),
],
[
'type' => 'link',
'title' => lang('Podcast.edit'),
'uri' => route_to('podcast-edit', $podcast->id),
],
])) ?>" />
</article>

View File

@ -10,7 +10,7 @@
<?= $this->section('content') ?>
<Alert glyph="alert" variant="danger"><?= lang('PodcastImport.warning') ?></Alert>
<Alert glyph="alert" variant="danger" class="max-w-xl"><?= lang('PodcastImport.warning') ?></Alert>
<form action="<?= route_to('podcast-import') ?>" method="POST" enctype='multipart/form-data' class="flex flex-col mt-6 gap-y-8">
<?= csrf_field() ?>

View File

@ -4,90 +4,17 @@
<a href="<?= route_to(
'episode-list',
$podcast->id,
) ?>" class="inline-flex items-center text-sm underline hover:no-underline">
) ?>" class="inline-flex items-center text-sm underline hover:no-underline focus:ring-castopod">
<?= lang('Podcast.see_all_episodes') ?>
<?= icon('chevron-right', 'ml-2') ?>
</a>
</header>
<?php if ($episodes): ?>
<div class="flex p-2 overflow-x-auto gap-x-6 snap snap-x snap-proximity">
<div class="grid px-4 py-2 -mx-2 overflow-x-auto grid-cols-latestEpisodes gap-x-4 snap snap-x snap-proximity">
<?php foreach ($episodes as $episode): ?>
<article class="snap-center flex flex-col flex-shrink-0 flex-1 w-full min-w-[12rem] max-w-[17rem] overflow-hidden bg-white border-2 border-pine-100 rounded-xl">
<div class="relative">
<?= publication_pill(
$episode->published_at,
$episode->publication_status,
'absolute top-2 right-2 text-sm'
); ?>
<img
src="<?= $episode->image->medium_url ?>"
alt="<?= $episode->title ?>" class="object-cover w-full" />
</div>
<div class="flex items-start justify-between p-2">
<div class="flex flex-col min-w-0">
<a href="<?= route_to(
'episode-view',
$podcast->id,
$episode->id,
) ?>"
class="text-sm font-semibold truncate hover:underline focus:ring-castopod"
>
<?= episode_numbering(
$episode->number,
$episode->season_number,
'text-xs font-semibold text-gray-600 !no-underline border px-1 border-gray-500 mr-1',
true,
) . $episode->title ?>
</a>
</div>
<button
type="button"
class="inline-flex items-center p-1 focus:ring-castopod"
id="more-dropdown-<?= $episode->id ?>"
data-dropdown="button"
data-dropdown-target="more-dropdown-<?= $episode->id ?>-menu"
aria-label="<?= lang('Common.more') ?>"
aria-haspopup="true"
aria-expanded="false"
><?= icon('more') ?></button>
<DropdownMenu id="more-dropdown-<?= $episode->id ?>-menu" labelledby="more-dropdown-<?= $episode->id ?>" items="<?= esc(json_encode([
[
'type' => 'link',
'title' => lang('Episode.edit'),
'uri' => route_to('episode-edit', $podcast->id, $episode->id),
],
[
'type' => 'link',
'title' => lang('Episode.embed.title'),
'uri' => route_to('embed-add', $podcast->id, $episode->id),
],
[
'type' => 'link',
'title' => lang('Person.persons'),
'uri' => route_to('episode-persons-manage', $podcast->id, $episode->id),
],
[
'type' => 'link',
'title' => lang('Episode.soundbites'),
'uri' => route_to('soundbites-edit', $podcast->id, $episode->id),
],
[
'type' => 'link',
'title' => lang('Episode.go_to_page'),
'uri' => route_to('episode', $podcast->handle, $episode->slug),
],
[
'type' => 'separator',
],
[
'type' => 'link',
'title' => lang('Episode.delete'),
'uri' => route_to('episode-delete', $podcast->id, $episode->id),
'class' => 'font-semibold text-red-600',
],
])) ?>" />
</div>
</article>
<?= view('episode/_card', [
'episode' => $episode,
]) ?>
<?php endforeach; ?>
</div>
<?php else: ?>

View File

@ -16,49 +16,12 @@
<?= $this->section('content') ?>
<div class="grid gap-4 grid-cols-podcasts">
<div class="grid gap-4 grid-cols-cards">
<?php if ($podcasts !== null): ?>
<?php foreach ($podcasts as $podcast): ?>
<article class="relative h-full overflow-hidden transition bg-white shadow border-3 border-pine-100 rounded-xl group hover:shadow-xl focus-within:ring-castopod">
<div class="w-full h-full overflow-hidden">
<img
alt="<?= $podcast->title ?>"
src="<?= $podcast->image->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-hover:scale-105" />
</div>
<a href="<?= route_to(
'podcast-view',
$podcast->id,
) ?>" class="absolute bottom-0 left-0 flex flex-col justify-end w-full h-full text-white" style="
background-image: linear-gradient(180deg,
hsla(0, 0%, 35.29%, 0) 0%,hsla(0, 0%, 34.53%, 0.034375) 16.36%,
hsla(0, 0%, 32.42%, 0.125) 33.34%,
hsla(0, 0%, 29.18%, 0.253125) 50.1%,
hsla(0, 0%, 24.96%, 0.4) 65.75%,
hsla(0, 0%, 19.85%, 0.546875) 79.43%,
hsla(0, 0%, 13.95%, 0.675) 90.28%,
hsla(0, 0%, 7.32%, 0.765625) 97.43%,
hsla(0, 0%, 0%, 0.8) 100%
);
">
<div class="px-4 pb-4 transition duration-75 ease-out translate-y-6 group-hover:translate-y-0">
<h2 class="font-bold leading-none truncate font-display"><?= $podcast->title ?></h2>
<p class="text-sm transition duration-150 opacity-0 group-hover:opacity-75">@<?= $podcast->handle ?></p>
</div>
</a>
<button class="absolute top-0 right-0 p-2 mt-2 mr-2 text-white transition -translate-y-12 rounded-full opacity-0 group-hover:translate-y-0 bg-black/25 group-hover:opacity-100" id="more-dropdown-<?= $podcast->id ?>" data-dropdown="button" data-dropdown-target="more-dropdown-<?= $podcast->id ?>-menu" aria-haspopup="true" aria-expanded="false" title="<?= lang('Common.more') ?>"><?= icon('more') ?></button>
<DropdownMenu id="more-dropdown-<?= $podcast->id ?>-menu" labelledby="more-dropdown-<?= $podcast->id ?>" items="<?= esc(json_encode([
[
'type' => 'link',
'title' => lang('Podcast.view'),
'uri' => route_to('podcast-view', $podcast->id),
],
[
'type' => 'link',
'title' => lang('Podcast.edit'),
'uri' => route_to('podcast-edit', $podcast->id),
],
])) ?>" />
</article>
<?= view('podcast/_card', [
'podcast' => $podcast,
]) ?>
<?php endforeach; ?>
<?php else: ?>
<p class="italic"><?= lang('Podcast.no_podcast') ?></p>

View File

@ -1,4 +1,4 @@
<div class="sticky top-0 left-0 z-50 flex items-center justify-between w-full h-10 text-white border-b shadow bg-pine-800 border-pine-900">
<div class="sticky top-0 left-0 z-50 flex items-center justify-between w-full h-10 text-white border-b bg-pine-800 border-pine-900">
<div class="inline-flex items-center h-full">
<a href="<?= route_to('home') ?>" class="inline-flex items-center h-full px-2 border-r border-pine-900 focus:ring-inset focus:ring-castopod">
<?= svg('castopod-logo-base', 'h-6') ?>
@ -20,7 +20,7 @@
aria-expanded="false"><div class="relative mr-1">
<?= icon('account-circle', 'text-3xl opacity-60') ?>
<?= user()
->podcasts === [] ? '' : '<img src="' . interact_as_actor()->avatar_image_url . '" class="absolute bottom-0 w-4 h-4 rounded-full -right-1" />' ?>
->podcasts === [] ? '' : '<img src="' . interact_as_actor()->avatar_image_url . '" class="absolute bottom-0 w-4 h-4 border rounded-full -right-1 border-pine-800" />' ?>
</div>
<?= user()->username ?>
<?= icon('caret-down', 'ml-auto text-2xl') ?></button>

View File

@ -24,9 +24,9 @@
<?php endif; ?>
<header class="py-8 text-white border-b bg-pine-800">
<div class="container flex flex-col px-2 py-4 mx-auto">
<div class="container flex flex-col items-start px-2 py-4 mx-auto">
<a href="<?= route_to('home') ?>"
class="inline-flex items-center mb-2"><?= icon(
class="inline-flex items-center mb-2 focus:ring-castopod"><?= icon(
'arrow-left',
'mr-2',
) . lang('Page.back_to_home') ?></a>
@ -42,7 +42,7 @@
<?= render_page_links() ?>
<small><?= lang('Common.powered_by', [
'castopod' =>
'<a class="underline hover:no-underline" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod</a>',
'<a class="underline hover:no-underline focus:ring-castopod" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod</a>',
]) ?></small>
</footer>
</body>

View File

@ -19,9 +19,9 @@
<div class="flex gap-x-2">
<img src="<?= $person->image->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-10 h-10 rounded-full" />
<div class="flex flex-col">
<h4 class="text-sm font-semibold hover:underline focus:ring-castopod">
<h4 class="text-sm font-semibold">
<?php if ($person->information_url): ?>
<a href="<?= $person->information_url ?>" target="_blank" rel="noopener noreferrer"><?= $person->full_name ?></a>
<a href="<?= $person->information_url ?>" class="hover:underline focus:ring-castopod" target="_blank" rel="noopener noreferrer"><?= $person->full_name ?></a>
<?php else: ?>
<?= $person->full_name ?>
<?php endif; ?>

View File

@ -19,7 +19,7 @@
<body class="flex" style="background: <?= $themeData['background'] ?>; color: <?= $themeData['text'] ?>;">
<img src="<?= $episode->image->thumbnail_url ?>" alt="<?= $episode->title ?>" class="flex-shrink w-36 h-36" />
<div class="flex flex-col items-start flex-1 min-w-0 px-4 pt-4 h-36">
<a href="https://castopod.org/" class="absolute text-2xl top-1 right-2 text-pine-500 hover:opacity-75" title="<?= lang('Common.powered_by', [
<a href="https://castopod.org/" class="absolute top-0 right-0 mt-1 mr-2 text-2xl text-pine-500 hover:opacity-75" title="<?= lang('Common.powered_by', [
'castopod' => 'Castopod',
]) ?>" target="_blank" rel="noopener noreferrer"><?= icon('podcasting/castopod') ?></a>
<div class="flex gap-x-2">

View File

@ -31,8 +31,8 @@
</div>
<?php endif; ?>
<nav class="flex items-center justify-between h-10 col-start-2 px-2 text-white bg-pine-800">
<a href="<?= route_to('podcast-episodes', $podcast->handle) ?>" class="inline-flex items-center focus:ring-castopod" title="<?= lang('Episode.back_to_episodes', [
<nav class="flex items-center justify-between h-10 col-start-2 text-white bg-pine-800">
<a href="<?= route_to('podcast-episodes', $podcast->handle) ?>" class="inline-flex items-center h-full px-2 focus:ring-castopod focus:ring-inset" title="<?= lang('Episode.back_to_episodes', [
'podcast' => $podcast->title,
]) ?>">
<?= icon('arrow-left', 'mr-2 text-lg') ?>
@ -46,7 +46,7 @@
</div>
</div>
</a>
<div class="inline-flex items-center self-end h-full gap-x-2">
<div class="inline-flex items-center self-end h-full px-2 gap-x-2">
<?php if (in_array(true, array_column($podcast->fundingPlatforms, 'is_visible'), true)): ?>
<IconButton glyph="heart" variant="accent" data-toggle="funding-links" data-toggle-class="hidden"><?= lang('Podcast.sponsor') . lang('Podcast.sponsor_title') ?></IconButton>
<?php endif; ?>
@ -75,7 +75,7 @@
<h1 class="inline-flex items-baseline max-w-md mt-2 text-2xl font-bold leading-none sm:text-3xl font-display line-clamp-2"><?= $episode->title ?></h1>
<div class="flex items-center mt-4 gap-x-8">
<?php if ($episode->persons !== []): ?>
<button class="flex items-center text-xs font-semibold gap-x-2 hover:underline" data-toggle="persons-list" data-toggle-class="hidden">
<button class="flex items-center text-xs font-semibold gap-x-2 hover:underline focus:ring-castopod" data-toggle="persons-list" data-toggle-class="hidden">
<div class="inline-flex flex-row-reverse">
<?php $i = 0; ?>
<?php foreach ($episode->persons as $person): ?>

View File

@ -16,7 +16,7 @@ $navigationItems = [
<nav class="sticky z-40 flex col-start-2 px-4 pt-4 bg-white shadow md:px-8 gap-x-2 md:gap-x-4 -top-4 rounded-conditional-b-xl">
<?php foreach ($navigationItems as $item): ?>
<?php $isActive = url_is($item['uri']); ?>
<a href="<?= $item['uri'] ?>" class="px-4 py-1 text-sm font-semibold uppercase border-b-4<?= $isActive ? ' border-b-4 text-pine-500 border-pine-500' : ' text-gray-500 hover:text-gray-900 hover:border-gray-200 border-transparent' ?>"><?= $item['label'] ?><span class="px-2 ml-1 font-semibold rounded-full <?= $isActive ? ' bg-pine-100' : ' bg-gray-100' ?>"><?= $item['labelInfo'] ?></span></a>
<a href="<?= $item['uri'] ?>" class="px-4 py-1 text-sm font-semibold uppercase focus:ring-castopod border-b-4<?= $isActive ? ' border-b-4 text-pine-500 border-pine-500' : ' text-gray-500 hover:text-gray-900 hover:border-gray-200 border-transparent' ?>"><?= $item['label'] ?><span class="px-2 ml-1 font-semibold rounded-full <?= $isActive ? ' bg-pine-100' : ' bg-gray-100' ?>"><?= $item['labelInfo'] ?></span></a>
<?php endforeach; ?>
<button type="button" class="p-2 ml-auto rotate-180 rounded-full md:hidden focus:ring-castopod" data-toggle="podcast-sidebar" data-toggle-class="absolute sticky top-0 right-0 hidden bg-white top-12"><?= icon('menu') ?></button>
</nav>

View File

@ -32,16 +32,19 @@
<h1 class="mb-2 text-xl"><?= lang('Home.all_podcasts') ?> (<?= count(
$podcasts,
) ?>)</h1>
<section class="grid gap-4 grid-cols-podcasts">
<section class="grid gap-4 grid-cols-cards">
<?php if ($podcasts): ?>
<?php foreach ($podcasts as $podcast): ?>
<a href="<?= $podcast->link ?>" class="w-full">
<article class="w-full h-full overflow-hidden bg-white border shadow rounded-xl hover:bg-gray-100 hover:shadow">
<img alt="<?= $podcast->title ?>"
src="<?= $podcast->image->medium_url ?>"
class="object-cover w-full h-48 mb-2" />
<h2 class="px-2 font-semibold leading-tight truncate"><?= $podcast->title ?></h2>
<p class="px-2 pb-2 text-gray-600">@<?= $podcast->handle ?></p>
<a href="<?= $podcast->link ?>" class="relative w-full h-full overflow-hidden transition shadow focus:ring-castopod rounded-xl border-pine-100 hover:shadow-xl focus:shadow-xl group border-3">
<article class="text-white">
<div class="absolute bottom-0 left-0 z-10 w-full h-full backdrop-gradient"></div>
<div class="w-full h-full overflow-hidden">
<img alt="<?= $podcast->title ?>" src="<?= $podcast->image->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" />
</div>
<div class="absolute bottom-0 left-0 z-20 px-4 pb-2">
<h2 class="font-bold leading-none truncate font-display"><?= $podcast->title ?></h2>
<p class="text-sm opacity-75">@<?= $podcast->handle ?></p>
</div>
</article>
</a>
<?php endforeach; ?>
@ -54,7 +57,7 @@
<?= render_page_links() ?>
<small><?= lang('Common.powered_by', [
'castopod' =>
'<a class="inline-flex font-semibold hover:underline" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
'<a class="inline-flex font-semibold hover:underline focus:ring-castopod" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
]) ?></small>
</footer>
</body>

View File

@ -23,9 +23,9 @@
<?php endif; ?>
<header class="py-8 text-white border-b bg-pine-800">
<div class="container flex flex-col px-2 py-4 mx-auto">
<div class="container flex flex-col items-start px-2 py-4 mx-auto">
<a href="<?= route_to('home') ?>"
class="inline-flex items-center mb-2"><?= icon(
class="inline-flex items-center mb-2 focus:ring-castopod"><?= icon(
'arrow-left',
'mr-2',
) . lang('Page.back_to_home') ?></a>
@ -39,7 +39,7 @@
<?= render_page_links() ?>
<small><?= lang('Common.powered_by', [
'castopod' =>
'<a class="inline-flex font-semibold hover:underline" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
'<a class="inline-flex font-semibold hover:underline focus:ring-castopod" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
]) ?></small>
</footer>
</body>

View File

@ -20,9 +20,9 @@
<?php endif; ?>
<header class="py-8 text-white border-b bg-pine-800">
<div class="container flex flex-col px-2 py-4 mx-auto">
<div class="container flex flex-col items-start px-2 py-4 mx-auto">
<a href="<?= route_to('home') ?>"
class="inline-flex items-center mb-2"><?= icon(
class="inline-flex items-center mb-2 focus:ring-castopod"><?= icon(
'arrow-left',
'mr-2',
) . lang('Page.back_to_home') ?></a>
@ -38,7 +38,7 @@
<?= render_page_links() ?>
<small><?= lang('Common.powered_by', [
'castopod' =>
'<a class="inline-flex font-semibold hover:underline" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
'<a class="inline-flex font-semibold hover:underline focus:ring-castopod" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
]) ?></small>
</footer>
</body>

View File

@ -18,7 +18,7 @@ $navigationItems = [
<nav class="sticky z-40 flex col-start-2 px-4 pt-8 bg-white shadow gap-x-2 md:gap-x-4 md:px-8 -top-8 md:-top-12 rounded-conditional-b-xl md:pt-12 ">
<?php foreach ($navigationItems as $item): ?>
<?php $isActive = url_is($item['uri']); ?>
<a href="<?= $item['uri'] ?>" class="px-4 py-1 text-sm font-semibold uppercase border-b-4<?= $isActive ? ' border-b-4 text-pine-500 border-pine-500' : ' text-gray-500 hover:text-gray-900 hover:border-gray-200 border-transparent' ?>"><?= $item['label'] ?></a>
<a href="<?= $item['uri'] ?>" class="px-4 py-1 text-sm font-semibold uppercase focus:ring-castopod border-b-4<?= $isActive ? ' border-b-4 text-pine-500 border-pine-500' : ' text-gray-500 hover:text-gray-900 hover:border-gray-200 border-transparent' ?>"><?= $item['label'] ?></a>
<?php endforeach; ?>
<button type="button" class="p-2 ml-auto rotate-180 rounded-full md:hidden focus:ring-castopod"><?= icon('menu') ?></button>
</nav>

View File

@ -1,7 +1,7 @@
<aside id="podcast-sidebar" class="sticky hidden col-span-1 md:block top-12">
<div class="absolute z-0 w-full h-full sm:hidden bg-pine-800/50"></div>
<div class="z-10 bg-pine-50">
<a href="<?= route_to('podcast_feed', $podcast->handle) ?>" class="inline-flex items-center mb-6 text-sm font-semibold text-pine-800 group" target="_blank" rel="noopener noreferrer">
<a href="<?= route_to('podcast_feed', $podcast->handle) ?>" class="inline-flex items-center mb-6 text-sm font-semibold focus:ring-castopod text-pine-800 group" target="_blank" rel="noopener noreferrer">
<?= icon('rss', ' mr-2 bg-orange-500 text-xl text-white group-hover:bg-orange-700 p-1 w-6 h-6 inline-flex items-center justify-center rounded-lg') . lang('Podcast.feed') ?>
</a>
<?php if (
@ -61,7 +61,7 @@
<p><?= $podcast->copyright ?></p>
<p><?= lang('Common.powered_by', [
'castopod' =>
'<a class="inline-flex font-semibold text-gray-500 hover:underline" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
'<a class="inline-flex font-semibold text-gray-500 hover:underline focus:ring-castopod" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
]) ?></p>
</div>
</footer>

View File

@ -48,7 +48,7 @@
<div class="flex items-center mt-4 gap-x-8">
<?php if ($podcast->persons !== []): ?>
<button class="flex items-center text-xs font-semibold gap-x-2 hover:underline" data-toggle="persons-list" data-toggle-class="hidden">
<button class="flex items-center text-xs font-semibold gap-x-2 hover:underline focus:ring-castopod" data-toggle="persons-list" data-toggle-class="hidden">
<div class="inline-flex flex-row-reverse">
<?php $i = 0; ?>
<?php foreach ($podcast->persons as $person): ?>

View File

@ -10,11 +10,11 @@
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
<?= service('vite')->asset('styles/index.css', 'css') ?>
<title><?= lang('Podcast.follow.title', [
<title><?= lang('Podcast.followTitle', [
'actorDisplayName' => $actor->display_name,
]) ?></title>
<meta name="description" content="<?= $actor->summary ?>"/>
<meta property="og:title" content="<?= lang('Podcast.follow.title', [
<meta property="og:title" content="<?= lang('Podcast.followTitle', [
'actorDisplayName' => $actor->display_name,
]) ?>"/>
<meta property="og:locale" content="<?= service(
@ -26,6 +26,8 @@
<?= service('vite')
->asset('styles/index.css', 'css') ?>
<?= service('vite')
->asset('js/podcast.ts', 'js') ?>
</head>
@ -38,7 +40,7 @@
<img src="<?= $actor->cover_image_url ?>" alt="" class="object-cover w-full h-32 bg-pine-800" />
<div class="flex px-4 py-2">
<img src="<?= $actor->avatar_image_url ?>" alt="<?= $actor->display_name ?>"
class="w-16 h-16 mr-4 -mt-8 rounded-full shadow-xl ring-2 ring-white" />
class="w-16 h-16 mr-4 -mt-8 rounded-full ring-2 ring-white" />
<div class="">
<p class="font-semibold"><?= $actor->display_name ?></p>
<p class="text-sm text-gray-500">@<?= $actor->username ?></p>
@ -58,7 +60,7 @@
hint="<?= lang('Fediverse.your_handle_hint') ?>"
required="true"
/>
<Button variant="primary" type="submit" class="self-end"><?= lang('Fediverse.follow.submit') ?></Button>
<Button variant="primary" type="submit" class="self-end" iconRight="send-plane"><?= lang('Fediverse.follow.submit') ?></Button>
</form>
</main>
@ -68,7 +70,7 @@
<p>
<?= lang('Common.powered_by', [
'castopod' =>
'<a class="inline-flex font-semibold hover:underline" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
'<a class="inline-flex font-semibold hover:underline focus:ring-castopod" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
]) ?>
</p>
</footer>

View File

@ -50,7 +50,7 @@
hint="<?= lang('Fediverse.your_handle_hint') ?>"
required="true" />
<Button variant="primary" type="submit" class="self-end"><?= lang('Fediverse.' . $action . '.submit') ?></Button>
<Button variant="primary" type="submit" class="self-end" iconRight="send-plane"><?= lang('Fediverse.' . $action . '.submit') ?></Button>
</form>
</main>
</body>

View File

@ -16,7 +16,7 @@
<header class="mb-4">
<a href="<?= route_to(
'home',
) ?>" class="inline-flex items-baseline text-4xl font-bold font-display text-pine-500">
) ?>" class="inline-flex items-baseline text-4xl font-bold font-display text-pine-500 focus:ring-castopod">
<?= 'castopod' . svg('castopod-logo', 'h-8 ml-2') ?>
</a>
</header>
@ -31,7 +31,7 @@
<?= $this->renderSection('footer') ?>
<small class="py-4 text-center border-t-2 border-pine-100"><?= lang('Common.powered_by', [
'castopod' =>
'<a class="inline-flex font-semibold hover:underline" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
'<a class="inline-flex font-semibold hover:underline focus:ring-castopod" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
]) ?></small>
</footer>
</body>

View File

@ -26,7 +26,7 @@
<footer class="container px-2 py-4 mx-auto text-sm text-right border-t">
<small><?= lang('Common.powered_by', [
'castopod' =>
'<a class="inline-flex font-semibold hover:underline" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
'<a class="inline-flex font-semibold hover:underline focus:ring-castopod" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>',
]) ?></small>
</footer>
</body>