body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #fafafa;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', 'Open Sans', system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
}
$border-width: 8px;
$stripe-width: 16px;
.mail-wrapper {
position: relative;
}
.mail {
position: relative;
border-radius: 8px;
padding: calc(1em + #{$border-width});
width: 520px;
background-color: #fff;
box-shadow:
inset 0 0 1px 1px rgb(9 0 80 / .2),
inset 0 -2px 1px rgb(9 0 80 / .1),
0 1px 2px rgb(9 0 80 / .2),
0 1px 8px rgb(9 0 80 / .1),
;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: repeating-linear-gradient(
45deg,
transparent 0 $stripe-width,
#d51122 0 $stripe-width * 2,
transparent 0 $stripe-width * 3,
#2770cb 0 $stripe-width * 4,
);
clip-path: polygon(
0% 0%,
0% 100%,
$border-width 100%,
$border-width $border-width,
calc(100% - #{$border-width}) $border-width,
calc(100% - #{$border-width}) calc(100% - #{$border-width}),
$border-width calc(100% - #{$border-width}),
$border-width 100%,
100% 100%,
100% 0%,
);
}
}
.mail-title {
margin-bottom: .5em;
font-weight: 600;
color: #2770cb;
}
.mail-field {
display: block;
width: 100%;
font-style: italic;
&:not(.content) {
margin-bottom: 8px;
background-image: linear-gradient(#a2a0ae 0 1px);
background-position-y: bottom;
background-size: 100% 1px;
background-repeat: no-repeat;
}
&.content {
background-image: repeating-linear-gradient(
to bottom,
transparent 0 calc(1.5em - 1px),
#a2a0ae calc(1.5em - 1px) 1.5em,
);
background-attachment: local;
resize: none;
}
&::placeholder {
font-style: normal;
color: #b5b2c6;
}
}
.mail-content-section {
display: flex;
column-gap: 1.5em;
align-items: stretch;
margin-top: 1.5em;
}
.mail-attachment-container {
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
position: relative;
border: 1px dashed #a2a0ae;
border-radius: 4px;
padding: 4px 8px;
width: 110px;
height: 110px;
transition-property: border-color, background-color;
transition-duration: .2s;
&:not(.filled):hover,
&.drag-over {
border-color: #2770cb;
background-color: rgb(39 112 203 / .1);
}
}
.mail-attachment-label-container {
display: flex;
flex-direction: column;
align-items: center;
gap: .5em;
text-align: center;
font-size: .5em;
font-weight: 500;
color: #b5b2c6;
transition-property: color;
transition-duration: .2s;
.mail-attachment-container:not(.filled):hover &,
.mail-attachment-container.drag-over & {
color: #2770cb;
}
}
.mail-attachment-label-icon {
width: 28px;
height: 28px;
stroke: currentColor;
}
.mail-attachment-wrapper {
position: absolute;
z-index: 2;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: scale(1) rotate(-15deg);
filter:
drop-shadow(0 1px 2px rgb(9 0 80 / .2))
drop-shadow(0 1px 8px rgb(9 0 80 / .1))
;
transition-property: transform, opacity;
transition-duration: .4s, .2s;
&.hidden {
transform: scale(0) rotate(0);
opacity: 0;
}
}
.mail-attachment {
--corner-size: 28px;
$border-width: 8px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
width: 100%;
height: 100%;
background-color: #fff;
clip-path: polygon(
calc(100% - var(--corner-size)) 0,
100% var(--corner-size),
100% 100%,
0 100%,
0 0,
);
overflow: hidden;
&::after {
content: '';
position: absolute;
bottom: 100%;
left: 100%;
border-radius: inherit;
width: 100%;
height: 100%;
transform: translate(calc(var(--corner-size) * -1), var(--corner-size));
background-image: linear-gradient(45deg, #fff, #b4b2c6);
box-shadow:
-1px 1px 2px rgb(9 0 80 / .2),
-1px 1px 8px rgb(9 0 80 / .1),
;
}
}
.mail-attachment-image {
border-radius: 4px;
width: calc(100% - 16px);
height: calc(100% - 16px);
object-fit: contain;
&.hidden {
display: none;
}
}
.mail-attachment-extension {
color: #b4b2c6;
&.hidden {
display: none;
}
}
.mail-attachment-remove-button {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
z-index: 4;
top: -10px;
right: -10px;
border-radius: 50%;
padding: 4px;
background-color: #fff;
opacity: 0;
box-shadow:
0 1px 2px rgb(9 0 80 / .2),
0 1px 8px rgb(9 0 80 / .1),
;
transition-property: transform, opacity;
transition-duration: .2s;
.mail-attachment-container:hover & {
opacity: 1;
}
&.hidden {
display: none;
}
}
.mail-attachment-remove-button-icon {
width: 12px;
height: 12px;
fill: #a1a1ad;
}
.mail-attachment-input {
position: absolute;
z-index: 1;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
&::file-selector-button {
display: none;
}
&.hidden {
display: none;
}
}
.mail-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 1.5em;
}
.mail-signature {
font-style: italic;
}
.mail-button {
border: 1px solid #1f5cbf;
border-radius: 4px;
padding: 4px 12px;
background-color: #2770cb;
color: #fff;
box-shadow:
inset 0 1px rgb(255 255 255 / .1),
inset 0 -1px rgb(0 0 0 / .1),
0 1px 2px rgb(9 0 80 / .2),
0 1px 8px rgb(9 0 80 / .1),
;
transition-property: transform, background-color;
transition-duration: .2s;
@media (hover: hover) {
&:hover {
background-color: #215cbd;
}
}
&:active {
transform: scale(.96);
}
}
.mail-paperclip {
position: absolute;
top: 60%;
right: -12px;
width: 80px;
transform: rotate(-15deg);
transition-property: transform, opacity;
transition-duration: .4s;
filter:
drop-shadow(0 1px 2px rgb(9 0 80 / .2))
drop-shadow(0 1px 8px rgb(9 0 80 / .1))
;
&.front {
z-index: 2;
}
&.back {
z-index: -1;
}
&.hidden {
transform: translateX(100%) rotate(15deg);
opacity: 0;
}
}
// notes:
// - underline scroll broken in firefox
// - can maybe animate corner with @property
// - accessibility labels for file input and remove button
// - accessibility labels for fields (placeholders are bad)
// - focus states
// - drag states for file input
View Compiled
const mailAttachmentInput = document.querySelector('.mail-attachment-input');
const mailAttachmentWrapper = document.querySelector('.mail-attachment-wrapper');
const mailAttachmentContainer = document.querySelector('.mail-attachment-container');
const mailAttachmentImage = document.querySelector('.mail-attachment-image');
const mailAttachmentExtension = document.querySelector('.mail-attachment-extension');
const mailAttachmentRemoveButton = document.querySelector('.mail-attachment-remove-button');
const mailPaperclipFront = document.querySelector('.mail-paperclip.front');
const mailPaperclipBack = document.querySelector('.mail-paperclip.back');
const removeImage = () => {
mailAttachmentImage.classList.add('hidden');
mailAttachmentImage.src = '';
}
mailAttachmentInput.addEventListener('change', (e) => {
const file = e.target.files[0];
const reader = new FileReader();
mailAttachmentRemoveButton.classList.remove('hidden');
mailAttachmentInput.classList.add('hidden');
mailAttachmentContainer.classList.add('filled');
reader.addEventListener('load', (e) => {
mailAttachmentImage.src = e.target.result;
mailAttachmentImage.classList.remove('hidden');
mailAttachmentWrapper.classList.remove('hidden');
mailPaperclipFront.classList.remove('hidden');
mailPaperclipBack.classList.remove('hidden');
});
if (file.type.startsWith('image/') || file.type === 'application/pdf') {
mailAttachmentExtension.classList.add('hidden');
reader.readAsDataURL(e.target.files[0]);
} else {
removeImage();
mailAttachmentWrapper.classList.remove('hidden');
mailPaperclipFront.classList.remove('hidden');
mailPaperclipBack.classList.remove('hidden');
const fileExtension = file.name.split('.').pop().toUpperCase();
mailAttachmentExtension.textContent = `.${fileExtension}`;
mailAttachmentExtension.classList.remove('hidden');
}
});
mailAttachmentRemoveButton.addEventListener('click', () => {
mailAttachmentInput.classList.remove('hidden');
mailAttachmentRemoveButton.classList.add('hidden');
mailAttachmentWrapper.classList.add('hidden');
mailAttachmentContainer.classList.remove('filled');
mailPaperclipFront.classList.add('hidden');
mailPaperclipBack.classList.add('hidden');
mailAttachmentWrapper.addEventListener('transitionend', () => {
mailAttachmentInput.value = '';
removeImage();
}, { once: true });
});
mailAttachmentInput.addEventListener('dragenter', () => {
mailAttachmentContainer.classList.add('drag-over');
});
mailAttachmentInput.addEventListener('dragleave', () => {
mailAttachmentContainer.classList.remove('drag-over');
});
mailAttachmentInput.addEventListener('drop', () => {
console.log('test')
mailAttachmentContainer.classList.remove('drag-over');
});