点击查看html编辑器说明文档

赛博朋克2077风格的页面设计 - Preactedit icon

|
|
Fork(复制)
|
|

👉 新版编辑器已上线,点击进行体验吧!

BUG反馈
嵌入
设置
下载
HTML
格式化
支持Emmet,输入 p 后按 Tab键试试吧!
<head> ...
展开
</head>
<body>
            
            <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-y/preact/10.5.7/preact.min.js" type="application/javascript"></script>

<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@500;700&amp;display=swap">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=VT323&amp;display=swap">


<div id="root"></div>

        
</body>
CSS
格式化
            
            * {
  box-sizing: border-box;
  position: relative;
}

html,
body {
  --colors-bg--300: #1e181e;
  --colors-bg--500: #191a1e;
  --colors-primary--200: #f4908b;
  --colors-primary--300: #ea6f69;
  --colors-primary--500: #e8615a;
  --colors-primary--600: #9c3230;
  --colors-primary--700: #5e2122;
  --colors-primary--800: #451717;
  --colors-primary--900: #3c181a;
  --colors-secondary--500: #2be4ea;
  --colors-secondary--900: #295459;
  --colors-tertiary--500: #fed33f;
  --colors-on_bg--500: var(--colors-primary--500);
  --colors-on_tertiary--500: var(--colors-bg--300);
  --colors-on_primary--500: var(--colors-primary--200);
  --colors-active--500: #2bfea0;
  --fonts-primary: "Rajdhani", sans-serif;
  --fonts-secondary: "VT323", monospace;
  --ui-glow: 0 0 5px var(--colors-primary--500);
  --ui-glow-borders--500: 0 0 3px var(--colors-primary--500);
  --ui-glow-color: currentcolor;
  --ui-glow-text: -9px -6px 40px var(--ui-glow-color);
  --ui-glow-text--dimmed: -9px -6px 40px var(--ui-glow-color);
  --ui-elevation--1: 2px 2px 0 rgba(0, 0, 0, 0.8);
  --ui-notch-amount: 1rem;
  --ui-notch-hypotenuse: 22.627416px; /* hypothenuse of --ui-notch-amount */
  --ui-notch-path: polygon(
    0 0,
    100% 0,
    100% calc(100% - var(--ui-notch-amount) + 2px),
    calc(100% - var(--ui-notch-amount) + 2px) 100%,
    0 100%
  );

  background-color: var(--colors-bg--500);
  background-image: radial-gradient(
    ellipse at 33% 10%,
    #461616 0%,
    transparent 75%
  );
  background-repeat: no-repeat;
  color: var(--colors-on_bg--500);
  font-family: var(--fonts-primary);
  font-size: 100%;
  line-height: 1.4;
  margin: 0;
  min-height: 100vh;
}

a {
  color: inherit;
  text-decoration: none;
}

.app-skeleton {
  padding: 0 1rem;
  height: 100vh;
  min-width: 1024px;
}

.app-header {
  align-items: center;
  display: flex;
  grid-area: header;
  justify-content: space-between;
  margin-bottom: 1rem;
  padding: 1rem 0 0.5rem 0;
}

.app-header::after {
  background-color: var(--colors-primary--500);
  box-shadow: var(--ui-glow);
  bottom: 0;
  content: "";
  height: 2px;
  position: absolute;
  left: 0;
  width: 100%;
}

.app-header__anchor {
  padding: 0.5rem;
}

.app-header__anchor__text {
  font-family: var(--fonts-secondary);
  font-size: 1.25rem;
  letter-spacing: 0.035rem;
  text-shadow: var(--ui-glow-text);
  text-transform: uppercase;
}

.app-container {
  display: grid;
  grid-gap: 1rem;
  grid-template-areas: "a main main main side";
  grid-template-columns: 280px 1fr 1fr 1fr 1fr;
  height: calc(100% - 5.25rem);
}

.app-a {
  grid-area: a;
  height: 100%;
}

.app-b {
  grid-area: side;
}

.app-main {
  grid-area: main;
}

.nav-section {
  padding-bottom: 0.25rem;
}

.nav-section__header {
  background-color: var(--colors-bg--300);
  border: 1px solid #5d2322;
  border-radius: 3px;
  margin-bottom: 0.5rem;
  padding: 0.5rem 0.75rem;
}

.nav-section__body {
  padding-left: 1rem;
}

.nav-section__title {
  color: #2be4ea;
  font-size: 1rem;
  margin: 0;
}

.nav {
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav__link {
  line-height: 1.4rem;
}

.nav__link__element + .nav__link__element {
  margin-left: 0.5rem;
}

.app-header .nav {
  align-items: flex-end;
  display: flex;
}

.app-header .nav__item + .nav__item {
  margin-left: 3rem;
}

.app-header .nav__link {
  align-items: baseline;
  display: flex;
  text-shadow: var(--ui-glow-text);
  text-transform: uppercase;
  transition: transform 0.25s;
}

.app-header .nav__link:hover {
  color: var(--colors-primary--200);
}

.app-header .nav__link.nav__link--active {
  color: var(--colors-secondary--500);
  font-size: 1.25rem;
  transform: none;
}

.nav-section .nav__item + .nav__item {
  margin-top: 0.125rem;
}

.nav-section .nav__link {
  border: 1px solid transparent;
  border-radius: 3px;
  color: var(--colors-secondary--500);
  display: block;
  font-family: var(--fonts-primary);
  padding: 0.5rem 0.75rem;
  transition: background-color 0.25s;
}

.nav-section .nav__link:focus,
.nav-section .nav__link:hover {
  background-color: var(--colors-bg--300);
  border: 1px solid #5d2322;
}

.nav-section .nav__link--active,
.nav-section .nav__link.nav__link--active:focus,
.nav-section .nav__link.nav__link--active:hover {
  background-color: #391419;
  border-color: #9c3230;
}

.channel-link,
.conversation-link {
  align-items: center;
  display: flex;
}

.channel-link__icon,
.conversation-link__icon {
  margin-right: 0.75rem;
}

.channel-link__element + .channel-link__element,
.conversation-link__element + .conversation-link__element {
  margin-left: 0.75rem;
}

.conversation-link__icon {
}

.conversation-link__icon::after {
  background-color: var(--colors-primary--500);
  border-radius: 50%;
  content: "";
  display: block;
  height: 0.5em;
  width: 0.5em;
}

.conversation-link--online .conversation-link__icon::after {
  background-color: var(--colors-active--500);
}

.channel-link--unread,
.conversation-link--unread {
  font-weight: bold;
}

.badge {
  border: 1.5px solid var(--colors-tertiary--500);
  border-radius: 3px;
  color: var(--colors-tertiary--500);
  display: inline-block;
  font-size: 0.92rem;
  font-weight: normal;
  line-height: 1;
  padding: 0.15em 0.34em;
  vertical-align: middle;
}

.slate__title {
  font-size: 1.125rem;
  margin: 0;
}

.pad {
  background-color: var(--colors-bg--300);
  border: 2px solid var(--colors-primary--600);
  clip-path: var(--ui-notch-path);
}

.pad__body {
  padding: 0.75rem;
  padding-bottom: var(--ui-notch-amount);
}

.pad::before {
  background-color: var(--colors-primary--600);
  bottom: 5px;
  content: "";
  display: block;
  height: 3px; /* bigger to compensate transform */
  position: absolute;
  right: -6px;
  top: auto;
  transform: rotate(-45deg);
  width: var(--ui-notch-hypotenuse);
  z-index: 100;
}

/* @component: form-* */
input,
textarea {
}

.form-label {
  background-color: var(--colors-primary--600);
  color: var(--colors-on_primary--500);
  font-family: var(--fonts-secondary);
  font-size: 0.92rem;
  display: inline-block;
  padding: 0.125em 0.75em;
  padding-right: 2em;
  letter-spacing: 0.065em;
  line-height: 1;
  text-transform: uppercase;
  vertical-align: bottom; /* inline-block fix */
  clip-path: polygon(
    0.25em 0,
    calc(100% - 1em) 0,
    100% 100%,
    0.33em 100%,
    0 100%,
    0 0.25em
  );
}

.form-control {
  background-color: var(--colors-bg--300);
  border: 1px solid var(--colors-primary--600);
  clip-path: var(--ui-notch-path);
  padding-right: 0.5rem;
}

.form-control::before {
  background-color: var(--colors-primary--600);
  bottom: 5px;
  content: "";
  display: block;
  height: 3px; /* bigger to compensate transform */
  position: absolute;
  right: -6px;
  top: auto;
  transform: rotate(-45deg);
  width: var(--ui-notch-hypotenuse);
  z-index: 100;
}

.form-control input,
.form-control textarea {
  background-color: transparent;
  border: 0;
  box-shadow: none;
  color: var(--colors-primary--500);
  font-family: var(--fonts-primary);
  font-size: 1rem;
  letter-spacing: 0.045em;
  outline: none;
  padding: 0.5rem;
  resize: none;
  width: 100%;
}

.form-control--with-addon {
  align-items: center;
  display: flex;
}

.form-control--with-addon .button,
.form-control--with-addon .button:hover {
  background-color: transparent;
  border-color: transparent;
}

.form-control__addon {
  display: inline-block;
  padding: 0.45rem;
}

.form-control__addon svg {
  display: block;
  fill: currentcolor;
  height: 1.125rem;
  opacity: 0.66;
  width: 1.125rem;
}

.form-control__addon--prefix {
  order: -1;
}

.form-control__addon--suffix {
  order: 1;
}

.form-search {
  margin-bottom: 0.75rem;
}

.message {
  padding-bottom: 1rem;
}

.message__body {
  background-color: var(--colors-bg--300);
  border: 1px solid var(--colors-tertiary--500);
  border-radius: 3px;
  color: var(--colors-tertiary--500);
  padding: 0.75rem;
}

.message__footer {
  color: var(--colors-tertiary--500);
  font-size: 0.85rem;
  margin-top: 0.25rem;
}

/* @component: button */
.button {
  background-color: transparent;
  border: 1px solid var(--colors-primary--500);
  border-radius: 2px;
  cursor: pointer;
  color: var(--colors-primary--500);
  display: inline-block;
  font-family: var(--fonts-primary);
  letter-spacing: 0.045em;
  padding: 0.45rem;
  text-align: left;
  text-transform: uppercase;
}

.button:hover {
  background-color: var(--colors-primary--800);
  color: var(--colors-on_primary--500);
}

.button__icon {
  display: block;
  height: 1.125rem;
  line-height: 1;
  vertical-align: top;
  width: 1.125rem;
}

.button__icon {
  fill: currentcolor;
}

.button__content {
  display: inline-block;
  z-index: 2;
}

.button--size-lg,
.button--size-xl {
  border-width: 2px;
  clip-path: var(--ui-notch-path);
  padding: 0.55rem 0.7rem 0.7rem 0.55rem;
}

.button--size-lg::before,
.button--size-xl::before {
  background-color: currentcolor;
  bottom: 5px;
  content: "";
  display: block;
  height: 3px; /* bigger to compensate transform */
  position: absolute;
  right: -6px;
  top: auto;
  transform: rotate(-45deg);
  width: var(--ui-notch-hypotenuse);
  z-index: 100;
}

.button--size-xl {
  padding: 0.55rem 1.5rem 1.5rem 0.55rem;
}

.button--primary {
  color: var(--colors-secondary--500);
  border-color: var(--colors-secondary--500);
}

.button--primary:hover {
  background-color: var(--colors-secondary--900);
  color: var(--colors-secondary--500);
}

.button-toolbar {
  display: flex;
}

.button-toolbar .button {
  display: block;
  border-radius: 0;
}

.button-toolbar .button + .button {
  margin-left: -1px;
}

.channel-feed {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.channel-feed__body {
  flex-grow: 1;
}

.channel-feed__footer {
}

.channel-message-form {
  align-items: flex-start;
  display: flex;
  padding-bottom: 1rem;
}

.channel-message-form .form-group {
  flex: 1;
}

.channel-message-form .form-footer {
  margin-left: 1rem;
  margin-top: 1.42rem;
}

.channel-message-form .form-control {
  width: 100%;
}

.text-heading1,
.text-heading2,
.text-heading3,
.text-heading4,
.text-heading5,
.text-heading6,
.text-paragraph1 {
  margin: 0;
}

.segment-topbar {
  display: flex;
  justify-content: space-between;
  margin-bottom: 1rem;
  min-height: 3.5rem;
}

.segment-topbar::after {
  background-color: var(--colors-primary--500);
  box-shadow: var(--ui-glow);
  bottom: 0;
  content: "";
  height: 1px;
  position: absolute;
  left: 0;
  width: 100%;
}

.segment-topbar__header {
  padding: 0.5rem 0.5rem;
  padding-top: 0;
}

.segment-topbar__overline {
  font-family: var(--fonts-secondary);
}

.segment-topbar__title {
  letter-spacing: 0.035em;
  text-shadow: var(--ui-glow-text--dimmed);
  text-transform: uppercase;
}

.segment-topbar__aside {
  align-self: flex-start;
  box-shadow: -6px -4px 24px rgba(156, 50, 48, 0.4);
}

        
JS
格式化
            
             /** @jsx h */
const { h, render } = preact;

function App() {
  return (
    <div className="app-skeleton">
      <header className="app-header">
        <div className="app-header__anchor">
          <span className="app-header__anchor__text">Night-City NetWire</span>
        </div>
        <nav>
          <ul className="nav">
            {FIXTURES.headerMenu.map((navItem, navItemIndex) => (
              <NavItem key={navItemIndex} navItem={navItem} />
            ))}
          </ul>
        </nav>
        <div />
      </header>
      <div className="app-container">
        <div className="app-a">
          <div className="segment-topbar">
            <div className="segment-topbar__header">
              <TextHeading3 className="segment-topbar__title">
                Messages
              </TextHeading3>
            </div>
            <div className="segment-topbar__aside">
              <div className="button-toolbar">
                <a className="button button--primary button--size-lg">
                  <IconFeedAdd className="button__icon" />
                </a>
              </div>
            </div>
          </div>

          <form className="form-search" onSubmit={(e) => e.preventDefault()}>
            <div className="form-group">
              <div className="form-control form-control--with-addon">
                <input name="query" placeholder="Search..." type="text" />
                <div className="form-control__addon form-control__addon--prefix">
                  <IconSearchSubmit />
                </div>
              </div>
            </div>
          </form>

          <NavSection renderTitle={(props) => <h2 {...props}>Feeds</h2>}>
            <ChannelNav
              activeChannel={{ id: "a0cc", name: "Watson" }}
              channels={FIXTURES.feed}
            />
          </NavSection>

          <NavSection renderTitle={(props) => <h2 {...props}>Direct</h2>}>
            <ConversationNav conversations={FIXTURES.conversation} />
          </NavSection>
        </div>
        <div className="app-main">
          <div className="channel-feed">
            <div className="segment-topbar">
              <div className="segment-topbar__header">
                <TextOverline className="segment-topbar__overline">
                  NetWire_Seed: d869db7fe62fb07c25a0403ecaea55031744b5fb
                </TextOverline>
                <TextHeading4 className="segment-topbar__title">
                  <ChannelLink name="Watson" />
                </TextHeading4>
              </div>
              <div className="segment-topbar__aside">
                <div className="button-toolbar">
                  <a className="button button--default">
                    <IconFeedMute className="button__icon" />
                  </a>
                  <a className="button button--default">
                    <IconFeedSettings className="button__icon" />
                  </a>
                  <a className="button button--default">
                    <IconMenuMore className="button__icon" />
                  </a>
                </div>
              </div>
            </div>
            <div className="channel-feed__body">
              <FeedMessage message={FIXTURES.messages[0]} />
              <FeedMessage message={FIXTURES.messages[0]} />
            </div>
            <div className="channel-feed__footer">
              <form
                className="channel-message-form"
                action="#"
                onSubmit={(e) => e.preventDefault()}
              >
                <div className="form-group">
                  <label className="form-label" for="message">
                    Message
                  </label>
                  <div className="form-control">
                    <textarea
                      id="message"
                      className="form-control"
                      name="message"
                    ></textarea>
                  </div>
                </div>
                <div className="form-footer">
                  <Button size="xl" type="submit" variant="primary">
                    Send
                  </Button>
                </div>
              </form>
            </div>
          </div>
        </div>
        <div className="app-b">
          <Pad>
            <TextHeading3 $as="h4">What's this?</TextHeading3>
            <TextParagraph1>
              A <em>fake</em> Slack or Discord type of app inspired by Cyberpunk
              2077. This app is static, eg. not implementing much logic.
            </TextParagraph1>
            <TextParagraph1>
              The goal is: showcasing a start of a UI kit. If you've played the
              game, you' might be able to pick-up some similarities with the
              in-game menus.
            </TextParagraph1>
          </Pad>
        </div>
      </div>
    </div>
  );
}

function NavSection({ children, renderTitle }) {
  return (
    <div className="nav-section">
      <div className="nav-section__header">
        {renderTitle({ className: "nav-section__title" })}
      </div>
      <div className="nav-section__body">{children}</div>
    </div>
  );
}

function FeedMessage({ message }) {
  return (
    <div className="message">
      <div className="message__body">
        <div>
          {
            "I got a gig lined up in Watson, no biggie. If you prove useful, expect more side gigs coming your way. I need a half-decent netrunner. Hit me up, provide credentials, eddies on completion."
          }
        </div>
      </div>
      <div className="message__footer">
        <span className="message__authoring">V. M. Vargas</span>
        {" - 11:04pm"}
      </div>
    </div>
  );
}

function ChannelNav({ activeChannel = null, channels = [] }) {
  return (
    <ul className="nav">
      {channels.map((channel) => (
        <li className="nav__item">
          <a
            className={`nav__link ${
              activeChannel && activeChannel.id === channel.id
                ? "nav__link--active"
                : ""
            }`}
            href="#"
          >
            <ChannelLink {...channel}>{name}</ChannelLink>
          </a>
        </li>
      ))}
    </ul>
  );
}

function ConversationNav({ activeConversation = null, conversations = [] }) {
  return (
    <ul className="nav">
      {conversations.map((convo) => (
        <li className="nav__item">
          <a
            className={`nav__link ${
              activeConversation && activeConversation.id === convo.id
                ? "nav__link--active"
                : ""
            }`}
            href="#"
          >
            <ConversationLink conversation={convo} />
          </a>
        </li>
      ))}
    </ul>
  );
}

function ChannelLink({ icon, name, unread }) {
  return (
    <span
      className={`channel-link ${
        unread > 0 ? "conversation-link--unread" : ""
      }`}
    >
      <span className="channel-link__icon">#</span>
      <span className="channel-link__element">{name}</span>

      {unread > 0 && (
        <span className="channel-link__element">
          <Badge>{unread}</Badge>
        </span>
      )}
    </span>
  );
}

function ConversationLink({ conversation }) {
  return (
    <span
      className={`conversation-link ${
        conversation.isOnline ? "conversation-link--online" : ""
      } ${conversation.unread > 0 ? "conversation-link--unread" : ""}`}
    >
      {conversation.members && conversation.members.length > 2 ? (
        <span className="conversation-link__icon" />
      ) : (
        <span className="conversation-link__icon" />
      )}

      <span className="conversation-link__element">{conversation.name}</span>

      {conversation.unread > 0 && (
        <span className="conversation-link__element">
          <Badge>{conversation.unread}</Badge>
        </span>
      )}
    </span>
  );
}

function Badge({ children }) {
  return <span className="badge">{children}</span>;
}

function Button({
  children,
  type = "button",
  size = "default",
  variant = "default"
}) {
  return (
    <button
      className={`button ${variant ? `button--${variant}` : ""} ${
        size ? `button--size-${size}` : ""
      }`}
      type={type}
    >
      <span className="button__content">{children}</span>
    </button>
  );
}

function Pad({ children, renderCap = null }) {
  return (
    <div className="pad">
      <div className="pad__body">{children}</div>
    </div>
  );
}

function NavItem({ navItem }) {
  return (
    <li className="nav__item">
      <a
        className={`nav__link ${navItem.isActive ? "nav__link--active" : ""}`}
        href="#"
      >
        <span className="nav__link__element">{navItem.text}</span>
        {navItem.notificationCount > 0 && (
          <span className="nav__link__element">
            <Badge>{navItem.notificationCount}</Badge>
          </span>
        )}
      </a>
    </li>
  );
}

function MakeTextBase(classNameDefault, $asDefault) {
  return ({ $as = null, children, className }) => {
    const AsComponent = $as || $asDefault;

    return (
      <AsComponent className={`${classNameDefault} ${className}`}>
        {children}
      </AsComponent>
    );
  };
}

const TextHeading1 = MakeTextBase("text-heading1", "h1");
const TextHeading2 = MakeTextBase("text-heading2", "h2");
const TextHeading3 = MakeTextBase("text-heading3", "h3");
const TextHeading4 = MakeTextBase("text-heading4", "h4");
const TextHeading5 = MakeTextBase("text-heading5", "h5");
const TextHeading6 = MakeTextBase("text-heading6", "h6");
const TextParagraph1 = MakeTextBase("text-paragraph1", "p");
const TextOverline = MakeTextBase("segment-topbar__overline", "span");

function MakeIcon(svg) {
  return ({ className }) => (
    <svg
      className={className}
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 24 24"
    >
      {svg}
    </svg>
  );
}

const IconFeedMute = MakeIcon(
  <path d="M18 9.5c2.481 0 4.5 1.571 4.5 3.503 0 1.674-1.703 3.48-4.454 3.48-.899 0-1.454-.156-2.281-.357-.584.358-.679.445-1.339.686.127-.646.101-.924.081-1.56-.583-.697-1.007-1.241-1.007-2.249 0-1.932 2.019-3.503 4.5-3.503zm0-1.5c-3.169 0-6 2.113-6 5.003 0 1.025.37 2.032 1.023 2.812.027.916-.511 2.228-.997 3.184 1.302-.234 3.15-.754 3.989-1.268.709.173 1.388.252 2.03.252 3.542 0 5.954-2.418 5.954-4.98.001-2.906-2.85-5.003-5.999-5.003zm-.668 6.5h-1.719v-.369l.938-1.361v-.008h-.869v-.512h1.618v.396l-.918 1.341v.008h.95v.505zm3.035 0h-2.392v-.505l1.306-1.784v-.011h-1.283v-.7h2.25v.538l-1.203 1.755v.012h1.322v.695zm-10.338 9.5c1.578 0 2.971-1.402 2.971-3h-6c0 1.598 1.45 3 3.029 3zm.918-7.655c-.615-1.001-.947-2.159-.947-3.342 0-3.018 2.197-5.589 5.261-6.571-.472-1.025-1.123-1.905-2.124-2.486-.644-.374-1.041-1.07-1.04-1.82v-.003c0-1.173-.939-2.123-2.097-2.123s-2.097.95-2.097 2.122v.003c.001.751-.396 1.446-1.041 1.82-4.667 2.712-1.985 11.715-6.862 13.306v1.749h9.782c.425-.834.931-1.764 1.165-2.655zm-.947-15.345c.552 0 1 .449 1 1 0 .552-.448 1-1 1s-1-.448-1-1c0-.551.448-1 1-1z" />
);

const IconFeedSettings = MakeIcon(
  <path d="M6 16h-6v-3h6v3zm-2-5v-10h-2v10h2zm-2 7v5h2v-5h-2zm13-7h-6v-3h6v3zm-2-5v-5h-2v5h2zm-2 7v10h2v-10h-2zm13 3h-6v-3h6v3zm-2-5v-10h-2v10h2zm-2 7v5h2v-5h-2z" />
);

const IconMenuMore = MakeIcon(
  <path d="M12 18c1.657 0 3 1.343 3 3s-1.343 3-3 3-3-1.343-3-3 1.343-3 3-3zm0-9c1.657 0 3 1.343 3 3s-1.343 3-3 3-3-1.343-3-3 1.343-3 3-3zm0-9c1.657 0 3 1.343 3 3s-1.343 3-3 3-3-1.343-3-3 1.343-3 3-3z" />
);

const IconFeedAdd = MakeIcon(
  <path d="M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z" />
);

const IconSearchSubmit = MakeIcon(
  <path d="M21.172 24l-7.387-7.387c-1.388.874-3.024 1.387-4.785 1.387-4.971 0-9-4.029-9-9s4.029-9 9-9 9 4.029 9 9c0 1.761-.514 3.398-1.387 4.785l7.387 7.387-2.828 2.828zm-12.172-8c3.859 0 7-3.14 7-7s-3.141-7-7-7-7 3.14-7 7 3.141 7 7 7z" />
);

const IconShop = MakeIcon(
  <path d="M16.53 7l-.564 2h-15.127l-.839-2h16.53zm-14.013 6h12.319l.564-2h-13.722l.839 2zm5.983 5c-.828 0-1.5.672-1.5 1.5 0 .829.672 1.5 1.5 1.5s1.5-.671 1.5-1.5c0-.828-.672-1.5-1.5-1.5zm11.305-15l-3.432 12h-13.017l.839 2h13.659l3.474-12h1.929l.743-2h-4.195zm-6.305 15c-.828 0-1.5.671-1.5 1.5s.672 1.5 1.5 1.5 1.5-.671 1.5-1.5c0-.828-.672-1.5-1.5-1.5z" />
);

const FIXTURES = {
  headerMenu: [
    { notificationCount: 0, text: "Home" },
    { isActive: true, notificationCount: 11, text: "Messages" },
    { notificationCount: 0, text: "Shop" },
    { notificationCount: 0, text: "Map" },
    { notificationCount: 0, text: "Files" }
  ],
  feed: [
    { id: "5ba5", name: "Afterlife", unread: 3 },
    { id: "4f22", name: "NCPD-Gigs" },
    { id: "fee9", name: "Pacifica" },
    { id: "a0cc", name: "Watson" },
    { id: "dee3", name: "_T_SQUAD", isPrivate: true, unread: 2 }
  ],
  conversation: [
    {
      id: "cc23",
      isOnline: true,
      unread: 5,
      name: "Rogue Amendiares"
    },
    { id: "95b4", isOnline: true, name: "Takemura", unread: 1 },
    { id: "10cf", name: "Wakado O., Regina Jones" },
    { id: "e466", name: "Dexter DeShawn" },
    { id: "ca0b", name: "Megabuilding H10 Administration" }
  ],
  messages: [
    {
      id: "fd0cf",
      content:
        "I got a gig lined up in Watson, no biggie. If you prove useful, expect more side gigs coming your way. I need a half-decent netrunner. Hit me up, provide credentials, eddies on completion.",
      dateTime: "2077-10-09T11:04:57Z",
      author: {
        id: "d12c",
        name: "V.M. Vargas"
      }
    }
  ]
};

render(<App />, document.getElementById("root"));

        
预览
控制台