Attachment
ui-attachment shows a file attached to a message or a form, with a small preview area, the file name, its size and
room for actions such as remove. The name is a link with the download attribute, so clicking it downloads the file from
href under that name. Long names are truncated with an ellipsis.
The preview goes in a ui-attachment-media child and the buttons in a ui-attachment-actions child. Both place
themselves before and after the name, whatever their position in the source.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Attachment demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/attachment.html"></import-element>
<ui-attachment name="quarterly-report.pdf" size="1.2 MB" href="data:text/plain,Quarterly%20report">
<ui-attachment-media>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M14 3H6v18h12V7Z" /><path d="M14 3v4h4" /></svg>
</ui-attachment-media>
</ui-attachment>
</body>
</html>Examples
With actions
Put buttons in ui-attachment-actions. Give icon buttons an aria-label that says what they do to which file.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Attachment demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
<style>
.demo-medium {
width: min(100%, calc(var(--spacing) * 120));
}
</style>
</head>
<body>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/attachment.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="./attachment-list-demo.html"></import-element>
<div class="demo-medium"><attachment-list-demo></attachment-list-demo></div>
</body>
</html><custom-element name="attachment-list-demo">
<script>
const files = useState({
list: [
{ id: 1, name: "design-review.fig", size: "4.8 MB", remove: "Remove design-review.fig" },
{ id: 2, name: "meeting-notes.md", size: "6 KB", remove: "Remove meeting-notes.md" },
],
});
function remove(file) {
files.list.splice(files.list.indexOf(file), 1);
}
</script>
<style>
ul {
display: flex;
flex-wrap: wrap;
gap: calc(var(--spacing) * 2);
margin: 0;
padding: 0;
list-style: none;
}
ui-button svg {
width: calc(var(--spacing) * 4);
height: calc(var(--spacing) * 4);
}
</style>
<ul>
<li repeat="#files.list" as="file" key="id">
<ui-attachment _name="#file.name" _size="#file.size">
<ui-attachment-media>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M14 3H6v18h12V7Z" /><path d="M14 3v4h4" /></svg>
</ui-attachment-media>
<ui-attachment-actions>
<ui-button variant="ghost" size="icon-xs" _aria-label="#file.remove" onclick="remove(file)">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12" /></svg>
</ui-button>
</ui-attachment-actions>
</ui-attachment>
</li>
</ul>
</custom-element>Image preview
ui-attachment-media is a rounded square with hidden overflow, and an <img> inside it covers the square, so it
works as a thumbnail.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Attachment demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/attachment.html"></import-element>
<ui-attachment name="sunset.svg" size="1 KB" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='%23f59e0b'/%3E%3Ccircle cx='16' cy='20' r='8' fill='%23f97316'/%3E%3C/svg%3E">
<ui-attachment-media>
<img alt="" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='%23f59e0b'/%3E%3Ccircle cx='16' cy='20' r='8' fill='%23f97316'/%3E%3C/svg%3E">
</ui-attachment-media>
</ui-attachment>
</body>
</html>In a message
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Attachment demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
<style>
.demo-medium {
width: min(100%, calc(var(--spacing) * 120));
}
</style>
</head>
<body>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/attachment.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/avatar.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/bubble.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/message.html"></import-element>
<div class="demo-medium">
<ui-message align="end">
<ui-message-avatar>
<ui-avatar><ui-avatar-fallback>ME</ui-avatar-fallback></ui-avatar>
</ui-message-avatar>
<ui-bubble align="end">Here is the contract.</ui-bubble>
<ui-attachment name="contract-final.pdf" size="320 KB" href="data:text/plain,Contract">
<ui-attachment-media>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M14 3H6v18h12V7Z" /><path d="M14 3v4h4" /></svg>
</ui-attachment-media>
</ui-attachment>
</ui-message>
</div>
</body>
</html>API
Parts
| Part | Slot | Renders |
|---|---|---|
ui-attachment |
(root) | The chip with the file name link and size. |
ui-attachment-media |
media |
A muted rounded square before the name for a file icon (<svg>, 16px) or a thumbnail <img>. |
ui-attachment-actions |
actions |
A row of buttons after the name, such as remove or open. |
The parts set their own slot, so they go directly inside ui-attachment with no attributes. The default slot of
each part takes any content.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
name |
string | File name. Shown as the link text and used as the download file name. |
|
size |
string | Human-readable size, such as 1.2 MB. Hidden when empty. |
|
href |
string | URL of the file. Without it the name is not a link. |
CSS parts
| Part | Description |
|---|---|
content |
The column with the name and size. |
title |
The file name link. |
Accessibility
The file name is a native link with download, so it is focusable and announced as a link when href is set. The
attachment shows a focus ring when anything inside it has focus. Mark icons in ui-attachment-media with
aria-hidden="true" and give thumbnails an empty alt when the file name already says what they are.