improved styles for manga page

This commit is contained in:
Daniel Bulant 2022-12-10 00:02:23 +01:00
parent 324133418f
commit d9bf3af9c0
6 changed files with 250 additions and 96 deletions

View file

@ -21,15 +21,21 @@
<style> <style>
div { div {
display: flex; display: grid;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.5rem; gap: 0.5rem;
justify-content: start; justify-content: start;
align-items: start; align-items: start;
grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
} }
div img { div img {
border-radius: 5px; border-radius: 5px;
height: 10rem; height: 10rem;
width: auto; width: auto;
} }
div img:first-child {
grid-column: 1 / span 2;
grid-row: 1 / span 2;
height: 20rem;
}
</style> </style>

View file

@ -3,7 +3,7 @@
export var selected = list[0]; export var selected = list[0];
</script> </script>
<div> <div class="tabs">
{#each list as item} {#each list as item}
<button on:click={() => selected = item} class:active={selected == item}>{item}</button> <button on:click={() => selected = item} class:active={selected == item}>{item}</button>
{/each} {/each}
@ -16,16 +16,24 @@
button { button {
border-radius: 0; border-radius: 0;
flex-grow: 1; flex-grow: 1;
padding: 0.5rem;
border: none;
background: none;
color: white;
font-size: 1.2rem;
cursor: pointer;
transition: background-color 0.2s, text-shadow 0.2s;
} }
button:first-child { button:first-child {
border-top-left-radius: 5px; border-radius: 0.5rem 0 0 0.5rem;
border-bottom-left-radius: 5px;
} }
button:last-child { button:last-child {
border-top-right-radius: 5px; border-radius: 0 0.5rem 0.5rem 0;
border-bottom-right-radius: 5px;
} }
button.active { button:hover, button:active, button:focus, button.active {
background: rgb(202, 202, 202); background: rgba(255,255,255,0.1);
outline: none;
text-shadow: 0 0 0.1rem white;
} }
</style> </style>

View file

@ -13,6 +13,8 @@
import SvelteMarkdown from 'svelte-markdown'; import SvelteMarkdown from 'svelte-markdown';
import ArtDialog from "$lib/components/artDialog.svelte"; import ArtDialog from "$lib/components/artDialog.svelte";
import streamSaver from "streamsaver"; import streamSaver from "streamsaver";
import Navbar from "./navbar.svelte";
import ExpandableDescription from "./expandableDescription.svelte";
export var data; export var data;
@ -287,73 +289,81 @@
var width; var width;
var smallScreenMode = width < 660; var smallScreenMode = width < 700;
$: smallScreenMode = width < 660; $: smallScreenMode = width < 700;
var scrollY, innerHeight;
</script> </script>
<svelte:window on:beforeUnload={beforeUnload} bind:innerWidth={width} /> <svelte:window on:beforeUnload={beforeUnload} bind:innerWidth={width} bind:scrollY bind:innerHeight />
<svelte:head> <svelte:head>
<title>Chapters of {title}</title> <title>{title} - Chapter list</title>
<meta name="description" value="Read {title} online, or download it as EPUB or CBZ file. Free of charge and ads." /> <meta name="description" value="Read {title} online, or download it as EPUB or CBZ file. Free of charge as well as free of ads." />
</svelte:head> </svelte:head>
{#if anilistData} {#await anilistData then data} <Navbar transparent={scrollY < 0.2*innerHeight} {title} />
{#if data.bannerImage}
<img class="banner" src={data.bannerImage} on:click={() => selectedImage = data.bannerImage} alt="">
{/if}
{/await} {/if}
<ArtDialog bind:selectedImage /> <ArtDialog bind:selectedImage />
{#if anilistData} {#await anilistData then data}
{#if data.bannerImage}
<div class="banner-container">
<img class="banner" src={data.bannerImage} on:click={() => selectedImage = data.bannerImage} alt="">
<div class="fader"></div>
</div>
{/if}
{/await} {/if}
<main class:smallScreenMode> <main class:smallScreenMode>
<h1>{title}</h1>
<h3>
{#if manga.altTitles.find(t => t.en)}
{manga.altTitles.find(t => t.en)?.en} &middot;
{/if}
{#if manga.year}
{manga.year} &middot;
{/if}
{#if anilistData} {#await anilistData then data} {data.status} &middot; {/await} {/if}
{manga.contentRating}
{#if smallScreenMode}
<br>
{#if relationships.find(t => t.type === "author")}
Author: {relationships.find(t => t.type === "author").attributes.name}
{#if relationships.find(t => t.type === "artist")} &middot; {/if}
{/if}
{#if relationships.find(t => t.type === "artist")}
Artist: {relationships.find(t => t.type === "artist").attributes.name}
{/if}
{/if}
</h3>
<div class="flex infoflex"> <div class="flex infoflex">
{#if relationships.find(t => t.type === "cover_art")} {#if relationships.find(t => t.type === "cover_art")}
<img class="cover" class:r18={!["safe", "suggestive"].includes(manga.contentRating)} draggable="false" src="{imageproxy}https://uploads.mangadex.org/covers/{mangaId}/{relationships.find(t => t.type === "cover_art").attributes.fileName}.512.jpg" alt="" on:click={() => selectedImage = `https://uploads.mangadex.org/covers/${mangaId}/${relationships.find(t => t.type === "cover_art").attributes.fileName}.512.jpg`}> <img class="cover" class:r18={!["safe", "suggestive"].includes(manga.contentRating)} draggable="false" src="{imageproxy}https://uploads.mangadex.org/covers/{mangaId}/{relationships.find(t => t.type === "cover_art").attributes.fileName}.512.jpg" alt="" on:click={() => selectedImage = `https://uploads.mangadex.org/covers/${mangaId}/${relationships.find(t => t.type === "cover_art").attributes.fileName}.512.jpg`}>
{/if} {/if}
<div class="info" class:hidden={smallScreenMode}> <div class="info">
<h1>{title}</h1>
<h3>
{#if manga.altTitles.find(t => t.en)}
{manga.altTitles.find(t => t.en)?.en} &middot;
{/if}
{#if manga.year}
{manga.year} &middot;
{/if}
{#if anilistData} {#await anilistData then data} {data.status} &middot; {/await} {/if}
{manga.contentRating}
</h3>
{#if relationships.find(t => t.type === "author")} {#if relationships.find(t => t.type === "author")}
<span class="block">Author: {relationships.find(t => t.type === "author").attributes.name}</span> <span class="block author">Author: {relationships.find(t => t.type === "author").attributes.name}</span>
{/if} {/if}
{#if relationships.find(t => t.type === "artist")} {#if relationships.find(t => t.type === "artist")}
<span class="block">Artist: {relationships.find(t => t.type === "artist").attributes.name}</span> <span class="block author">Artist: {relationships.find(t => t.type === "artist").attributes.name}</span>
{/if} {/if}
{#if relationships.find(t => t.related === "colored" && t.type === "manga")} {#if relationships.find(t => t.related === "colored" && t.type === "manga")}
<a href="/{relationships.find(t => t.related === "colored" && t.type === "manga").id}" class="block">Colored version</a> <a href="/{relationships.find(t => t.related === "colored" && t.type === "manga").id}" class="block">Colored version</a>
{/if} {/if}
{#if manga.description.en} {#if !smallScreenMode && manga.description.en}
<p><SvelteMarkdown source={manga.description.en} isInline /></p> <p class="description"><SvelteMarkdown source={manga.description.en} isInline /></p>
{/if} {/if}
</div> </div>
</div> </div>
{#if smallScreenMode && manga.description.en}
<div class="fulldescription">
<ExpandableDescription source={manga.description.en} />
</div>
{/if}
{#if manga.tags}
<div class="tags">
{#each manga.tags as tag}
<span class="tag">{tag.attributes.name.en || tag.attributes.name.jp || Object.values(tag.attributes.name)[0]}</span>
{/each}
</div>
{/if}
<div class="flex"> <div class="flex">
<div class="linklist"> <div class="linklist">
<a href="..">Go back to search page</a> <br>
<a href="https://mangadex.org/title/{mangaId}">Mangadex.org</a> <a href="https://mangadex.org/title/{mangaId}">Mangadex.org</a>
</div> </div>
<div class="copyright-header" class:copyright-header-active={copyrightOpen} on:click={() => copyrightOpen = !copyrightOpen}>Copyright infringement? (click)</div> <div class="copyright-header" class:copyright-header-active={copyrightOpen} on:click={() => copyrightOpen = !copyrightOpen}>Copyright infringement? (click)</div>
@ -446,19 +456,6 @@
AL score: {data.averageScore} <br> AL score: {data.averageScore} <br>
Also known as: {data.synonyms.join(", ")} {Object.values(manga.title).filter(t => t !== title).join(", ")} Also known as: {data.synonyms.join(", ")} {Object.values(manga.title).filter(t => t !== title).join(", ")}
{#if smallScreenMode}
{#if relationships.find(t => t.type === "author")}
<span class="block">Author: {relationships.find(t => t.type === "author").attributes.name}</span>
{/if}
{#if relationships.find(t => t.type === "artist")}
<span class="block">Artist: {relationships.find(t => t.type === "artist").attributes.name}</span>
{/if}
{#if manga.description.en}
<p><SvelteMarkdown source={manga.description.en} isInline /></p>
{/if}
{/if}
<br><br> <br><br>
{/await} {/if} {/await} {/if}
@ -510,28 +507,30 @@
{/if} {/if}
</div> </div>
{/if} {/if}
{#if manga.tags}
<div>
<h4>Tags</h4>
{#each manga.tags as tag}
<span class="block">{tag.attributes.name.en || tag.attributes.name.jp || Object.values(tag.attributes.name)[0]}</span>
{/each}
</div>
{/if}
</div> </div>
</div> </div>
</SwiperSlide> </SwiperSlide>
</Swiper> </Swiper>
</main> </main>
<style lang="postcss"> <style>
.tags {
display: flex;
overflow: auto;
}
.tag {
margin: 5px;
padding: 5px;
border-radius: 5px;
background-color: rgb(64,64,64);
user-select: all;
}
.hidden { .hidden {
display: none; display: none;
} }
.infoflex { .infoflex {
margin: 15px; margin: 15px;
} }
h4 { margin: 0; }
.flex-wrapped { .flex-wrapped {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -547,11 +546,26 @@
.cover.r18:hover { .cover.r18:hover {
filter: blur(0); filter: blur(0);
} }
.banner-container {
width: 100%;
max-height: 40vh;
position: absolute;
z-index: 0;
user-select: none;
}
.banner { .banner {
width: 100%; width: 100%;
max-height: 30vh; max-height: 100%;
object-fit: cover; object-fit: cover;
animation: reveal 2s cubic-bezier(0, 0, 0.08, 0.99); }
.banner-container .fader {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
background: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 100%);
} }
.genre { .genre {
border-radius: 5px; border-radius: 5px;
@ -562,29 +576,14 @@
.tabbed { .tabbed {
min-height: 20rem; min-height: 20rem;
} }
@media (prefers-reduced-motion) {
.banner {
animation: none;
}
}
@keyframes reveal {
from {
max-height: 0;
}
to {
max-height: 30vh;
}
}
.cover { .cover {
border-radius: 10px; border-radius: 10px;
height: 350px; height: 20rem;
margin-right: 15px; margin-right: 15px;
transition: height .3s;
} }
.smallScreenMode .cover { .smallScreenMode .cover {
margin: 0; height: 12rem;
}
.smallScreenMode .infoflex {
justify-content: center;
} }
.block { .block {
display: block; display: block;
@ -592,12 +591,23 @@
.flex { .flex {
display: flex; display: flex;
} }
h3 {
text-align: center;
margin-top: 0;
}
h1 { h1 {
margin-bottom: 0; margin: 0;
padding-top: 0.5rem;
text-align: left;
}
.smallScreenMode h1 {
font-size: 1.5rem;
}
h3 {
margin: 0;
}
.smallScreenMode h3 {
font-size: 1.2rem;
font-weight: normal;
}
.author {
font-weight: bold;
} }
.flex { .flex {
display: flex; display: flex;
@ -642,6 +652,10 @@
main { main {
font-size: 1.1rem; font-size: 1.1rem;
padding-bottom: 1rem; padding-bottom: 1rem;
position: relative;
z-index: 1;
/* background: linear-gradient(to bottom, rgba(0,0,0,0.3) 0vh, rgba(0,0,0,1) 30vh); */
padding-top: 5rem;
} }
.no-wrap { .no-wrap {
white-space: nowrap; white-space: nowrap;

View file

@ -0,0 +1,66 @@
<script lang="ts">
import SvelteMarkdown from "svelte-markdown";
export var source: string;
export var expanded = false;
</script>
<div class="description" class:expanded>
<div class="content">
<SvelteMarkdown {source} />
</div>
<div class="expander">
<button on:click={() => expanded = !expanded}>
{expanded ? "^" : "v"}
</button>
</div>
</div>
<style>
.description {
padding: 0 1rem 1rem 1rem;
font-size: 1.2rem;
line-height: 1.5rem;
/* limit to 5 rows when not expanded */
max-height: calc(2*1rem + 1.5rem * 5);
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 5;
-webkit-box-orient: vertical;
position: relative;
}
.expanded.description {
max-height: none;
-webkit-line-clamp: initial;
}
.expander {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 4rem;
background: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,1));
text-align: center;
}
.expander button {
background: none;
border: none;
color: white;
font-size: 2rem;
cursor: pointer;
}
.expanded .expander {
background: none;
bottom: -1rem;
}
</style>

View file

@ -0,0 +1,4 @@
<script lang="ts">
</script>

View file

@ -0,0 +1,56 @@
<script lang="ts">
export var transparent: boolean = false;
export var title: string;
</script>
<div class="navbar" class:transparent>
<a href="/"></a>
<span class="title">{title}</span>
</div>
<style>
.navbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 5rem;
background: rgb(0,0,0,0.9);
backdrop-filter: blur(25px);
z-index: 100;
display: flex;
align-items: center;
gap: 1rem;
padding: 0 1rem;
transition: background 0.3s ease;
user-select: none;
}
.transparent.navbar {
background: none;
backdrop-filter: none;
}
.navbar a {
color: white;
font-size: 2rem;
transform: translateX(0);
transition: transform 0.2s ease;
}
.navbar a:hover, .navbar a:active, .navbar a:focus {
text-decoration: none;
transform: translateX(-0.5rem);
}
.transparent .title {
opacity: 0;
user-select: none;
}
.title {
user-select: inherit;
opacity: 1;
transition: opacity 0.3s ease;
font-size: 1.5rem;
color: white;
font-weight: bold;
flex-grow: 1;
text-align: center;
}
</style>