UX kazesploro de superrigarda paĝo

Kiel mi projektis la superrigardan paĝon por ĉiuj postkategorioj

UI & UX-celoj

Mia ĉefa celo kun superrigarda paĝo, kiu montras ĉiujn kategoriojn por publikigitaj afiŝoj, estis pliigi malkovreblecon de la kategorioj vere disponeblaj. Mi ankaŭ vidis ĉi tiun ŝanĝon kiel ŝanco plibonigi la vidan belecon de la paĝo kaj ankaŭ akordigi la UX kun la aliaj paĝoj de ĉi tiu progresema interreta programo.

La sekva ekrankopio montras kiel aspektis la paĝo antaŭ la restrukturado.

Image e8edca7d7ea0

Kio devis ŝanĝiĝi

La malnova efektivigo havis du gravajn problemojn. Unue, la aranĝo efektive nevalidis, ĉar kategorioj kun malpli ol 5 elementoj skaligis la disponeblajn postantaŭrigardajn kovrilajn bildojn por kongrui kun la tuta larĝo. Due, ne estis ĉefa aga stango videbla ĉe la supro de la paĝo - certe estu unu redonita, ĉar ĝi provizas ĉefajn navigajn elementojn al la uzanto.

Krome evidentiĝas, ke la superrigarda paĝo por kategorioj ne efektive bonege faris ilin videblaj. Anstataŭe la uzanto ĉefe vidis kovrilajn bildojn por afiŝoj rilataj al ĉiu kategorio. Nur kiam vi rigardas duan rigardon, la vico kun ikono same kiel titolo de ĉiu kategorio estas agnoskita. Krom esti estetike ne plaĉa, ĉi tiu UI simple estis malbone desegnita.

Korektante erarojn

Ĉar ĉi tio estas mia persona retejo, mi ankaŭ estis tiu, kiu komence fuŝis aferojn sur la superrigarda paĝo de kategorioj. Sen pluaj ekskuzoj, mi do sidiĝis por prilabori la tutan paĝon.

La nova versio montras ununuran kolumnon de vicoj, kie ĉiu ero montras grandan antaŭrigardan bildon por kategorio. La laŭmenda bildo prenas la tutan permesitan larĝon. Centrita sur ambaŭ aksoj estas la kategoria titolo en la mezo, kiu igas ĝin tuj videbla kaj rekonebla. Por plibonigi legeblecon, mi aldonis subtilan gradienton de nigra al travidebla sub la teksto.

Ŝvebante super la bildo kaj teksto, aldona informteksto fariĝas videbla, montrante la tutan nombron de afiŝoj por ĉi tiu kategorio - informo, kiu antaŭe ne estis disponebla.

Por solvi la navigadan problemon, mi ankaŭ recikligis la ekzistantan agobreton kun ĉiuj ĉefaj vojoj haveblaj al la uzanto.

Image 0616bdd11b98

Pliboniĝante per ripeto

Por provizi pli agrablan sperton de uzanto, mi ankaŭ aldonis subtilan skalan-kuraĝigon al la bildo dum ŝvebado super kategoria vico. Dum la bildo grimpas ene de siaj originaj limoj, la vido ne nenecese kreskas.

Krome, nova vico sub la kategoria bildo fariĝas videbla per kuraĝigo post ŝvebado super ĝi. Ĉi tiu vico montras elekton de la plej novaj afiŝoj por la kategorio. Ĉiu post-antaŭvido estas ligo al la afiŝo. Ĉiu alia interaga elemento en la kategoria vico malfermas la detalan paĝon por la kategorio.

Por doni la iluzion pri pli da enhavo ol efektive ekzistas, mi uzis Daisy-UIs "stakan" klason por doni al ĉiu post-antaŭvido falsitan stakon por implici impreson de pli da antaŭvido-eroj sub la nunaj.

Image 41c052a8c928

Sur malgrandaj aparatoj, neniuj antaŭrigardoj aperas. Uzanto povas nur alklaki la grandan kategorian bildon por iri al la detala paĝo, kie ĉiuj afiŝoj estas listigitaj.

Revizio kaj perspektivo

Kiel vi povas vidi, eĉ simpla paĝo, kiu nur devas doni superrigardon de elementoj, povas esti fonto de vidaj kaj konceptaj eraroj kaj ankaŭ lernomaterialo pri kiel desegni bonan kaj funkciantan UI kaj UX.

Mi ne aldonis lokan tekstoserĉon por uzantoj por filtri per tekstenigo. Nuntempe mi intencas antaŭenigi esploradon sur la paĝo per paĝrulado kaj ŝvebado. Se ĉi tio fariĝos problemo en la estonteco (ekzemple ĉar uzantoj ne povas trovi la serĉatan enhavon), mi aldonos iujn filtrilojn. Ĉi tio povus ankaŭ fariĝi necesa, ĉar la nombro de afiŝoj kaj kategorioj daŭre kreskas. Kompreneble, mi bonvenigas vin provi la paĝon mem.

La fontkodo

La sekva teksto enhavas la kompletan ne modifitan kodon por la paĝa ujo kaj la kategorioj. Ĉio estas skribita per Tajpskribo kun React.js kaj Tailwind.css. Se mi pensas, ke vi povas reuzi iujn partojn, mi feliĉas, ke mi povus helpi vin.

//
// 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>
  );
};