UI és UX célok
A fő célom a közzétett bejegyzések összes kategóriáját megjelenítő áttekintő oldallal az volt, hogy növeljem a ténylegesen elérhető kategóriák felfedezhetőségét. Azt is láttam ennek a változásnak, hogy lehetőség nyílik az oldal vizuális szépségének fokozására, valamint az UX összehangolására a progresszív webes alkalmazás többi oldalával.
Az alábbi képernyőkép azt mutatja, hogyan nézett ki az oldal az újratervezés előtt.
Min kellett változtatni
A régi megvalósításnak két nagy problémája volt. Először is, az elrendezés valójában érvénytelen volt, mivel az 5 elemnél kevesebb kategória méretezte a rendelkezésre álló előnézeti borítóképeket a teljes szélességre. Másodszor, az oldal tetején nem volt látható elsődleges műveletsáv - feltétlenül egy rendereltnek kell lennie, mivel ez biztosítja a fő navigációs elemeket a felhasználó számára.
Továbbá nyilvánvalóvá válik, hogy a kategóriák áttekintő oldala valójában nem tett nagyszerű munkát azok láthatóvá tételéért. Ehelyett a felhasználó elsősorban az egyes kategóriákhoz kapcsolódó bejegyzések borító képeit látta. Csak egy második pillantáskor ismeri fel az egyes sorok ikont és címet. Eltekintve attól, hogy esztétikailag nem kellemes, ez a felhasználói felület egyszerűen rosszul lett megtervezve.
A hibák kijavítása
Mivel ez a személyes webalkalmazásom, én voltam az, aki kezdetben elrontotta a dolgokat a kategóriák áttekintő oldalán. Minden további kifogás nélkül leültem az egész oldal átdolgozására.
Az új verzió egyetlen sor oszlopot jelenít meg, ahol minden elem egy kategória nagy előnézeti képét mutatja. Az egyéni kép a teljes megengedett szélességet veszi át. Mindkét tengelyen középen található a kategória címe középen, ami azonnal láthatóvá és felismerhetővé teszi. Az olvashatóság javítása érdekében finom színátmenetet tettem hozzá a feketétől az átlátszóig a szöveg alá.
Ha a képre és a szövegre mutat az egérrel, megjelenik egy további információs szöveg, amely megmutatja a kategória bejegyzéseinek teljes számát - olyan információt, amely korábban nem volt elérhető.
A navigációs probléma megoldásához a meglévő műveletsávot is újra felhasználtam a felhasználó számára elérhető összes fő útvonallal.
Javítás iterációval
A kellemesebb felhasználói élmény biztosítása érdekében egy finom skálázási animációt is hozzáadtam a képhez, amikor egy kategória sor fölé mutattam. Ahogy a kép az eredeti határain belül skálázódik, a nézet nem nő fölöslegesen.
Ezenkívül a kategóriakép alatti új sor animáción keresztül válik láthatóvá, ha az egér fölé viszi. Ez a sor a kategória legújabb bejegyzéseit mutatja. Minden utólagos előnézet egy link a bejegyzéshez. A kategória-sor minden más interaktív eleme megnyitja a kategória részletes oldalát.
Annak érdekében, hogy a ténylegesnél több tartalom illúzióját kelthessem, a Daisy-UIs „stack” osztályát használtam, hogy minden utólagos előnézetet hamis kötegként adjak meg, hogy több előnézeti elem benyomását kelthessem a jelenlegi alatt.
Kis eszközökön nem jelennek meg előnézetek. A felhasználó csak a nagy kategóriájú képre kattintva juthat el a részletek oldalára, ahol az összes bejegyzés szerepel.
Áttekintés és kilátások
Amint láthatja, még egy egyszerű oldal is, amely csak áttekintést nyújt az elemekről, mind vizuális, mind fogalmi hibák forrása lehet, valamint tanulási anyag a jó és működő felhasználói felület, valamint az UX megtervezéséről.
Nem adtam hozzá helyi szöveges keresést a felhasználók számára a szövegbevitel szerinti szűréshez. Egyelőre görgetéssel és lebegéssel kívánom népszerűsíteni az oldal felfedezését. Ha ez a jövőben problémává válik (például azért, mert a felhasználók nem találják a keresett tartalmat), hozzáadok néhány szűrési lehetőséget. Erre is szükség lehet, mivel a bejegyzések és a kategóriák száma folyamatosan növekszik. Természetesen üdvözlöm, hogy próbálja ki az oldalt.
A forráskód
A következő szöveg tartalmazza az oldal tárolójának és a kategóriacsoportoknak a teljes, módosítatlan kódját. Minden Typescriptben van írva React.js és Tailwind.css fájlokkal. Ha úgy gondolja, hogy néhány részt újra felhasználhat, örülök, hogy segíthetek.
//
// Page container.
//
import React from "react";
import { CMSCategoryResolution } from "../../cms/entities/cms.entity.catgory";
import Reveal from "../Reveal/Reveal";
import { BogPostCategoryPostsGroup } from "./BlogPostCategoryPostsGroup";
import BlogPostCategoriesOverviewHero from "./BlogPostCategoriesOverviewHero";
import PrimaryActionBar from "../PrimaryActionsBar/PrimaryActionsBar";
/*
*
* Interfaces.
*
*/
interface Props {
categories: CMSCategoryResolution[];
}
/*
*
* Components.
*
*/
export default function BlogPostCategoriesOverview(props: Props) {
const { categories } = props;
return (
<div className="max-w-6xl mx-auto">
<BlogPostCategoriesOverviewHero />
<div className="mt-10 mb-20">
<PrimaryActionBar isCentered color="white" />
</div>
<div className="px-6 mx-auto space-y-6 lg:px-0 md:space-y-16 lg:space-y-20">
{categories.map((c, i) => (
<Reveal key={i}>
<BogPostCategoryPostsGroup category={c} maxPostsEachThreshold={20} />
</Reveal>
))}
</div>
</div>
);
}
//
// BogPostCategoryPostsGroup.
//
import React from "react";
import { CMSCategoryResolution } from "../../cms/entities/cms.entity.catgory";
import { FLPath } from "../../models/route/model.route";
import { CMSCategoryIconFactory } from "../CMS/CMSCategoryIcon";
import Link from "next/link";
import RiMore from "remixicon-react/MoreLineIcon";
/*
*
* Interfaces.
*
*/
interface Props {
category: CMSCategoryResolution;
maxPostsEachThreshold: number;
}
/*
*
* Components.
*
*/
export const BogPostCategoryPostsGroup = (props: Props) => {
const { category, maxPostsEachThreshold } = props;
const { posts } = category;
if (posts.length === 0) {
return null;
}
const url = `${FLPath.Categories}/${category.slug.current}`;
const Icon = CMSCategoryIconFactory({ variant: category.slug.current });
return (
<div className="flex flex-col items-center group">
<div className="relative max-w-4xl overflow-visible rounded-md">
<div className="absolute items-end justify-start hidden w-full h-full px-6 space-x-4 overflow-x-scroll duration-500 ease-in-out translate-y-12 opacity-0 md:flex pb-14 group-hover:opacity-100 group-hover:translate-y-20">
{posts.slice(0, 4).map((p, i) => (
<span className="stack">
<Link
href={`${FLPath.Posts}/${p.slug.current}`}
aria-label={p.title}
prefetch={false}>
<a className="duration-200 ease-out hover:scale-105">
<img
src={p.imageUrl.jpg}
alt={p.title}
loading="lazy"
className="object-contain h-20 rounded "
/>
</a>
</Link>
{Array.from({ length: 2 }, (_, i) => (
<span key={i} style={{ width: 152, height: 80 }} className="bg-gray-800 rounded" />
))}
</span>
))}
{posts.length >= 5 && (
<Link passHref href={url} aria-label={`Link to ${category.title}`} prefetch={false}>
<a className="flex flex-col items-center justify-center h-20 w-14">
<RiMore />
</a>
</Link>
)}
</div>
<div className="relative flex flex-col items-center overflow-hidden duration-500 ease-in-out rounded-md shadow-xl md:group-hover:-translate-y-20">
<img
className="object-cover w-full duration-500 ease-in-out group-hover:scale-105"
width={960}
height={200}
src={category.imageUrl.webp}
alt={category.title}
loading="lazy"
/>
<Link passHref href={url} aria-label={`Link to ${category.title}`} prefetch={false}>
<a className="absolute flex flex-col items-center justify-center w-full h-full bg-gradient-to-t from-primary">
<span className="flex items-center space-x-2 duration-300 ease-in-out translate-y-2 group-hover:-translate-y-3">
<Icon size="2em" />
<h2 className="text-sm md:text-xl">{category.title}</h2>
</span>
<span className="text-sm font-light text-gray-200 transition duration-500 ease-in-out translate-y-2 opacity-0 group-hover:opacity-100 group-hover:-translate-y-2">
{category.posts.length}
{category.posts.length === maxPostsEachThreshold ? "+" : ""}{" "}
{category.posts.length === 1 ? "post" : "posts"}
</span>
</a>
</Link>
</div>
</div>
</div>
);
};