Chat
An example of a UI that displays content in a chat conversation style. It is built with Lism primitives and a small amount of CSS (b--chat).
Overview
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation.
<Grid class="b--chat b--chat--speak" data-chat-dir="start"> <Frame class="b--chat_avatar" aria-hidden="true"> <img src="https://cdn.lism-css.com/img/avatar/EkABPzB34XR7.webp" alt="" width="60" height="60" decoding="async" /> </Frame> <Lism class="b--chat_name">Taro</Lism> <Lism class="b--chat_body"> <Decorator class="b--chat_deco" util="cbox" isSkipFlow /> <Flow class="b--chat_content" util="cbox"> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation. </p> </Flow> </Lism></Grid><div class="b--chat b--chat--speak l--grid" data-chat-dir="start"> <div class="b--chat_avatar l--frame" aria-hidden="true"> <img src="https://cdn.lism-css.com/img/avatar/EkABPzB34XR7.webp" alt="" width="60" height="60" decoding="async" /> </div> <div class="b--chat_name">Taro</div> <div class="b--chat_body"> <div class="b--chat_deco a--decorator is--skipFlow u--cbox" aria-hidden="true"></div> <div class="b--chat_content l--flow u--cbox"> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation. </p> </div> </div></div>Styles
The Chat base styles are defined in the following CSS. Copy this CSS into your project to use it.
If you copy it with the @layer lism-block wrapper as is, it fits into the core CSS layer order and can be overridden from the lism-custom layer.
@layer lism-block {
.b--chat {
--cbox-bgPct: 8%;
--keycolor: var(--gray);
gap: 0 calc(var(--deco-size) + 1px);
grid-template-areas: 'avatar name' 'avatar body';
grid-template-columns: auto 1fr;
grid-template-rows: minmax(1.5rem, auto) auto;
justify-items: start;
--avator-size: clamp(48px, 32px + 5cqw, 64px); /* @320px:40px ~ @640:64px */
--deco-size: calc(min(var(--avator-size), 80px) / 4 + 0.25rem);
--deco-mask: none;
--deco-t: 0;
--deco-scale: 1;
--deco-inset-x: 0;
}
.b--chat_avatar {
grid-area: avatar;
width: var(--avator-size);
aspect-ratio: 1/1;
border-radius: var(--bdrs--99);
background-color: var(--base);
}
.b--chat_name {
grid-area: name;
align-self: end;
padding-block: var(--s5);
padding-inline: var(--s10);
color: var(--text-2);
font-size: var(--fz--2xs);
font-style: italic;
--hl: 0px;
}
.b--chat_body {
grid-area: body;
position: relative;
}
.b--chat_content {
justify-self: start;
max-width: var(--sz--s);
padding: var(--s20);
border-radius: var(--bdrs--30);
--hl: var(--hl--s);
/* 吹き出し内の flow 間隔は控えめに(l--flow が参照する --flow--base を上書き) */
--flow--base: var(--flow--s);
}
.b--chat_deco {
position: absolute;
top: var(--deco-t);
inset-inline: var(--deco-inset-x);
width: var(--deco-size);
height: var(--deco-size);
scale: var(--deco-scale);
mask: var(--deco-mask) center / contain no-repeat;
}
.b--chat--speak {
--deco-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8"><path d="M8,8L8,8C7.4,4.8,5.4,2,2.6,0.4L2,0h6V8z"/></svg>');
}
.b--chat--think {
--deco-t: min(calc(var(--avator-size) / 3), 1.5rem);
--deco-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8"><circle cx="1.5" cy="1.7" r="1.5"/><circle cx="4.5" cy="5.2" r="2.5"/></svg>');
}
[data-chat-dir='start'] {
--deco-inset-x: auto calc(100% - 1px);
}
[data-chat-dir='end'] {
--deco-scale: -1 1;
--deco-inset-x: calc(100% - 1px) auto;
grid-template-areas: 'name avatar' 'body avatar';
grid-template-columns: 1fr auto;
justify-items: end;
}
[data-chat-dir='end'] .b--chat_content {
justify-self: end;
}
}
/* NOTE: 以下は @layer 外に配置(セレクタの詳細度を確保するため) */
/* speak バリアントの角丸調整: 左側 */
.b--chat--speak[data-chat-dir='start'] > div > .b--chat_content {
border-start-start-radius: 0;
}
/* speak バリアントの角丸調整: 右側 */
.b--chat--speak[data-chat-dir='end'] > div > .b--chat_content {
border-start-end-radius: 0;
} Markup
The markup consists of the following classes and attributes.
| Class / Attribute | Description |
|---|---|
b--chat |
Root element. Used together with l--grid, plus the b--chat--speak (speech) or b--chat--think (thought) modifier. |
data-chat-dir |
Display position. Use start (avatar on the left) or end (avatar on the right). |
b--chat_avatar |
Element displaying the avatar image (used with l--frame; optional). |
b--chat_name |
Element displaying the name (optional). |
b--chat_body |
Wrapper element for the speech bubble. |
b--chat_deco |
Tail decoration of the bubble (used with a--decorator + u--cbox). |
b--chat_content |
Element containing the content (used with l--flow + u--cbox). |
--keycolor |
Key color of the bubble. Can be overridden via the style attribute (defaults to var(--gray)). |
Examples
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut.
Lorem ipsum dolor sit amet. consectetur adipisicing elit, sed do eiusmod tempor. Non facere Laudantium ex eos doloribus aut dolore nisi provident.
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut.
<Grid class="b--chat b--chat--speak" data-chat-dir="start"> <Frame class="b--chat_avatar" aria-hidden="true"> <img src="https://cdn.lism-css.com/img/avatar/EkABPzB34XR7.webp" alt="" width="60" height="60" decoding="async" /> </Frame> <Lism class="b--chat_name">Taro</Lism> <Lism class="b--chat_body"> <Decorator class="b--chat_deco" util="cbox" isSkipFlow /> <Flow class="b--chat_content" util="cbox"> <p>Lorem ipsum dolor sit amet, ...</p> </Flow> </Lism></Grid><Grid class="b--chat b--chat--speak" keycolor="green" data-chat-dir="end"> <Frame class="b--chat_avatar" aria-hidden="true"> <img src="https://cdn.lism-css.com/img/avatar/of5v8rPFMI8F.webp" alt="" width="60" height="60" decoding="async" /> </Frame> <Lism class="b--chat_name">Hanako</Lism> <Lism class="b--chat_body"> <Decorator class="b--chat_deco" util="cbox" isSkipFlow /> <Flow class="b--chat_content" util="cbox"> <p>Lorem ipsum dolor <i>sit amet</i>. ...</p> <p>Lorem ipsum dolor sit amet, ...</p> </Flow> </Lism></Grid><div class="b--chat b--chat--speak l--grid" data-chat-dir="start"> <div class="b--chat_avatar l--frame" aria-hidden="true"> <img src="https://cdn.lism-css.com/img/avatar/EkABPzB34XR7.webp" alt="" width="60" height="60" decoding="async" /> </div> <div class="b--chat_name">Taro</div> <div class="b--chat_body"> <div class="b--chat_deco a--decorator is--skipFlow u--cbox" aria-hidden="true"></div> <div class="b--chat_content l--flow u--cbox"> <p>Lorem ipsum dolor sit amet, ...</p> </div> </div></div><div class="b--chat b--chat--speak l--grid" style="--keycolor:var(--green)" data-chat-dir="end"> <div class="b--chat_avatar l--frame" aria-hidden="true"> <img src="https://cdn.lism-css.com/img/avatar/of5v8rPFMI8F.webp" alt="" width="60" height="60" decoding="async" /> </div> <div class="b--chat_name">Hanako</div> <div class="b--chat_body"> <div class="b--chat_deco a--decorator is--skipFlow u--cbox" aria-hidden="true"></div> <div class="b--chat_content l--flow u--cbox"> <p>Lorem ipsum dolor <i>sit amet</i>. ...</p> <p>Lorem ipsum dolor sit amet, ...</p> </div> </div></div>Thought style (b--chat--think)
Use b--chat--think instead of b--chat--speak to represent thoughts — speech the character thinks but doesn’t say aloud.
b--chat--thinkLorem ipsum dolor sit amet.
Lorem ipsum dolor sit amet. consectetur adipisicing elit, sed do eiusmod tempor. Non facere Laudantium ex eos doloribus aut dolore nisi provident.
<Grid class="b--chat b--chat--think" keycolor="blue" data-chat-dir="start"> <Frame class="b--chat_avatar" aria-hidden="true"> <img src="https://cdn.lism-css.com/img/avatar/EkABPzB34XR7.webp" alt="" width="60" height="60" decoding="async" /> </Frame> <Lism class="b--chat_name">Name B</Lism> <Lism class="b--chat_body"> <Decorator class="b--chat_deco" util="cbox" isSkipFlow /> <Flow class="b--chat_content" util="cbox"> <p>Lorem ipsum dolor sit amet.</p> </Flow> </Lism></Grid><Grid class="b--chat b--chat--think" keycolor="purple" data-chat-dir="end"> <Frame class="b--chat_avatar" aria-hidden="true"> <img src="https://cdn.lism-css.com/img/avatar/of5v8rPFMI8F.webp" alt="" width="60" height="60" decoding="async" /> </Frame> <Lism class="b--chat_name">Name B</Lism> <Lism class="b--chat_body"> <Decorator class="b--chat_deco" util="cbox" isSkipFlow /> <Flow class="b--chat_content" util="cbox"> <p>Lorem ipsum dolor <i>sit amet</i>. ...</p> </Flow> </Lism></Grid><div class="b--chat b--chat--think l--grid" style="--keycolor:var(--blue)" data-chat-dir="start"> <div class="b--chat_avatar l--frame" aria-hidden="true"> <img src="https://cdn.lism-css.com/img/avatar/EkABPzB34XR7.webp" alt="" width="60" height="60" decoding="async" /> </div> <div class="b--chat_name">Name B</div> <div class="b--chat_body"> <div class="b--chat_deco a--decorator is--skipFlow u--cbox" aria-hidden="true"></div> <div class="b--chat_content l--flow u--cbox"> <p>Lorem ipsum dolor sit amet.</p> </div> </div></div><div class="b--chat b--chat--think l--grid" style="--keycolor:var(--purple)" data-chat-dir="end"> <div class="b--chat_avatar l--frame" aria-hidden="true"> <img src="https://cdn.lism-css.com/img/avatar/of5v8rPFMI8F.webp" alt="" width="60" height="60" decoding="async" /> </div> <div class="b--chat_name">Name B</div> <div class="b--chat_body"> <div class="b--chat_deco a--decorator is--skipFlow u--cbox" aria-hidden="true"></div> <div class="b--chat_content l--flow u--cbox"> <p>Lorem ipsum dolor <i>sit amet</i>. ...</p> </div> </div></div>