#box-shadow
Explore tagged Tumblr posts
jcmarchi · 5 months ago
Text
How to Create Multi-Step Forms With Vanilla JavaScript and CSS
New Post has been published on https://thedigitalinsider.com/how-to-create-multi-step-forms-with-vanilla-javascript-and-css/
How to Create Multi-Step Forms With Vanilla JavaScript and CSS
Multi-step forms are a good choice when your form is large and has many controls. No one wants to scroll through a super-long form on a mobile device. By grouping controls on a screen-by-screen basis, we can improve the experience of filling out long, complex forms.
But when was the last time you developed a multi-step form? Does that even sound fun to you? There’s so much to think about and so many moving pieces that need to be managed that I wouldn’t blame you for resorting to a form library or even some type of form widget that handles it all for you.
But doing it by hand can be a good exercise and a great way to polish the basics. I’ll show you how I built my first multi-step form, and I hope you’ll not only see how approachable it can be but maybe even spot areas to make my work even better.
We’ll walk through the structure together. We’ll build a job application, which I think many of us can relate to these recent days. I’ll scaffold the baseline HTML, CSS, and JavaScript first, and then we’ll look at considerations for accessibility and validation.
I’ve created a GitHub repo for the final code if you want to refer to it along the way.
The structure of a multi-step form
Our job application form has four sections, the last of which is a summary view, where we show the user all their answers before they submit them. To achieve this, we divide the HTML into four sections, each identified with an ID, and add navigation at the bottom of the page. I’ll give you that baseline HTML in the next section.
Navigating the user to move through sections means we’ll also include a visual indicator for what step they are at and how many steps are left. This indicator can be a simple dynamic text that updates according to the active step or a fancier progress bar type of indicator. We’ll do the former to keep things simple and focused on the multi-step nature of the form.,
The structure and basic styles
We’ll focus more on the logic, but I will provide the code snippets and a link to the complete code at the end.
Let’s start by creating a folder to hold our pages. Then, create an index.html file and paste the following into it:
Open HTML
<form id="myForm"> <section class="group-one" id="one"> <div class="form-group"> <div class="form-control"> <label for="name">Name <span style="color: red;">*</span></label> <input type="text" id="name" name="name" placeholder="Enter your name"> </div> <div class="form-control"> <label for="idNum">ID number <span style="color: red;">*</span></label> <input type="number" id="idNum" name="idNum" placeholder="Enter your ID number"> </div> </div> <div class="form-group"> <div class="form-control"> <label for="email">Email <span style="color: red;">*</span></label> <input type="email" id="email" name="email" placeholder="Enter your email"> </div> <div class="form-control"> <label for="birthdate">Date of Birth <span style="color: red;">*</span></label> <input type="date" id="birthdate" name="birthdate" max="2006-10-01" min="1924-01-01"> </div> </div> </section> <section class="group-two" id="two"> <div class="form-control"> <label for="document">Upload CV <span style="color: red;">*</span></label> <input type="file" name="document" id="document"> </div> <div class="form-control"> <label for="department">Department <span style="color: red;">*</span></label> <select id="department" name="department"> <option value="">Select a department</option> <option value="hr">Human Resources</option> <option value="it">Information Technology</option> <option value="finance">Finance</option> </select> </div> </section> <section class="group-three" id="three"> <div class="form-control"> <label for="skills">Skills (Optional)</label> <textarea id="skills" name="skills" rows="4" placeholder="Enter your skills"></textarea> </div> <div class="form-control"> <input type="checkbox" name="terms" id="terms"> <label for="terms">I agree to the terms and conditions <span style="color: red;">*</span></label> </div> <button id="btn" type="submit">Confirm and Submit</button> </section> <div class="arrows"> <button type="button" id="navLeft">Previous</button> <span id="stepInfo"></span> <button type="button" id="navRight">Next</button> </div> </form> <script src="script.js"></script>
Looking at the code, you can see three sections and the navigation group. The sections contain form inputs and no native form validation. This is to give us better control of displaying the error messages because native form validation is only triggered when you click the submit button.
Next, create a styles.css file and paste this into it:
Open base styles
:root --primary-color: #8c852a; --secondary-color: #858034; body font-family: sans-serif; line-height: 1.4; margin: 0 auto; padding: 20px; background-color: #f4f4f4; max-width: 600px; h1 text-align: center; form background: #fff; padding: 40px; border-radius: 5px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); display: flex; flex-direction: column; .form-group display: flex; gap: 7%; .form-group > div width: 100%; input:not([type="checkbox"]), select, textarea width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; .form-control margin-bottom: 15px; button display: block; width: 100%; padding: 10px; color: white; background-color: var(--primary-color); border: none; border-radius: 4px; cursor: pointer; font-size: 16px; button:hover background-color: var(--secondary-color); .group-two, .group-three display: none; .arrows display: flex; justify-content: space-between align-items: center; margin-top: 10px; #navLeft, #navRight width: fit-content; @media screen and (max-width: 600px) .form-group flex-direction: column;
Open up the HTML file in the browser, and you should get something like the two-column layout in the following screenshot, complete with the current page indicator and navigation.
Adding functionality with vanilla JavaScript
Now, create a script.js file in the same directory as the HTML and CSS files and paste the following JavaScript into it:
Open base scripts
const stepInfo = document.getElementById("stepInfo"); const navLeft = document.getElementById("navLeft"); const navRight = document.getElementById("navRight"); const form = document.getElementById("myForm"); const formSteps = ["one", "two", "three"]; let currentStep = 0; function updateStepVisibility() formSteps.forEach((step) => document.getElementById(step).style.display = "none"; ); document.getElementById(formSteps[currentStep]).style.display = "block"; stepInfo.textContent = `Step $currentStep + 1 of $formSteps.length`; navLeft.style.display = currentStep === 0 ? "none" : "block"; navRight.style.display = currentStep === formSteps.length - 1 ? "none" : "block"; document.addEventListener("DOMContentLoaded", () => navLeft.style.display = "none"; updateStepVisibility(); navRight.addEventListener("click", () => if (currentStep < formSteps.length - 1) currentStep++; updateStepVisibility(); ); navLeft.addEventListener("click", () => if (currentStep > 0) currentStep--; updateStepVisibility(); ); );
This script defines a method that shows and hides the section depending on the formStep values that correspond to the IDs of the form sections. It updates stepInfo with the current active section of the form. This dynamic text acts as a progress indicator to the user.
It then adds logic that waits for the page to load and click events to the navigation buttons to enable cycling through the different form sections. If you refresh your page, you will see that the multi-step form works as expected.
Multi-step form navigation
Let’s dive deeper into what the Javascript code above is doing. In the updateStepVisibility() function, we first hide all the sections to have a clean slate:
formSteps.forEach((step) => document.getElementById(step).style.display = "none"; );
Then, we show the currently active section:
document.getElementById(formSteps[currentStep]).style.display = "block";`
Next, we update the text that indicators progress through the form:
stepInfo.textContent = `Step $currentStep + 1 of $formSteps.length`;
Finally, we hide the Previous button if we are at the first step and hide the Next button if we are at the last section:
navLeft.style.display = currentStep === 0 ? "none" : "block"; navRight.style.display = currentStep === formSteps.length - 1 ? "none" : "block";
Let’s look at what happens when the page loads. We first hide the Previous button as the form loads on the first section:
document.addEventListener("DOMContentLoaded", () => navLeft.style.display = "none"; updateStepVisibility();
Then we grab the Next button and add a click event that conditionally increments the current step count and then calls the updateStepVisibility() function, which then updates the new section to be displayed:
navRight.addEventListener("click", () => if (currentStep < formSteps.length - 1) currentStep++; updateStepVisibility(); );
Finally, we grab the Previous button and do the same thing but in reverse. Here, we are conditionally decrementing the step count and calling the updateStepVisibility():
navLeft.addEventListener("click", () => if (currentStep > 0) currentStep--; updateStepVisibility(); );
Handling errors
Have you ever spent a good 10+ minutes filling out a form only to submit it and get vague errors telling you to correct this and that? I prefer it when a form tells me right away that something’s amiss so that I can correct it before I ever get to the Submit button. That’s what we’ll do in our form.
Our principle is to clearly indicate which controls have errors and give meaningful error messages. Clear errors as the user takes necessary actions. Let’s add some validation to our form. First, let’s grab the necessary input elements and add this to the existing ones:
const nameInput = document.getElementById("name"); const idNumInput = document.getElementById("idNum"); const emailInput = document.getElementById("email"); const birthdateInput = document.getElementById("birthdate") const documentInput = document.getElementById("document"); const departmentInput = document.getElementById("department"); const termsCheckbox = document.getElementById("terms"); const skillsInput = document.getElementById("skills");
Then, add a function to validate the steps:
Open validation script
function validateStep(step)
Here, we check if each required input has some value and if the email input has a valid input. Then, we set the isValid boolean accordingly. We also call a showError() function, which we haven’t defined yet.
Paste this code above the validateStep() function:
function showError(input, message) const formControl = input.parentElement; const errorSpan = formControl.querySelector(".error-message"); input.classList.add("error"); errorSpan.textContent = message;
Now, add the following styles to the stylesheet:
Open validation styles
input:focus, select:focus, textarea:focus outline: .5px solid var(--primary-color); input.error, select.error, textarea.error outline: .5px solid red; .error-message font-size: x-small; color: red; display: block; margin-top: 2px; .arrows color: var(--primary-color); font-size: 18px; font-weight: 900; #navLeft, #navRight display: flex; align-items: center; gap: 10px; #stepInfo color: var(--primary-color);
If you refresh the form, you will see that the buttons do not take you to the next section till the inputs are considered valid:
Finally, we want to add real-time error handling so that the errors go away when the user starts inputting the correct information. Add this function below the validateStep() function:
Open real-time validation script
function setupRealtimeValidation() nameInput.addEventListener("input", () => if (nameInput.value.trim() !== "") clearError(nameInput); ); idNumInput.addEventListener("input", () => if (idNumInput.value.trim() !== "") clearError(idNumInput); ); emailInput.addEventListener("input", () => if (emailInput.validity.valid) clearError(emailInput); ); birthdateInput.addEventListener("change", () => if (birthdateInput.value !== "") clearError(birthdateInput); ); documentInput.addEventListener("change", () => if (documentInput.files[0]) clearError(documentInput); ); departmentInput.addEventListener("change", () => if (departmentInput.value !== "") clearError(departmentInput); ); termsCheckbox.addEventListener("change", () => if (termsCheckbox.checked) clearError(termsCheckbox); );
This function clears the errors if the input is no longer invalid by listening to input and change events then calling a function to clear the errors. Paste the clearError() function below the showError() one:
function clearError(input) const formControl = input.parentElement; const errorSpan = formControl.querySelector(".error-message"); input.classList.remove("error"); errorSpan.textContent = "";
And now the errors clear when the user types in the correct value:
The multi-step form now handles errors gracefully. If you do decide to keep the errors till the end of the form, then at the very least, jump the user back to the erroring form control and show some indication of how many errors they need to fix.
Handling form submission
In a multi-step form, it is valuable to show the user a summary of all their answers at the end before they submit and to offer them an option to edit their answers if necessary. The person can’t see the previous steps without navigating backward, so showing a summary at the last step gives assurance and a chance to correct any mistakes.
Let’s add a fourth section to the markup to hold this summary view and move the submit button within it. Paste this just below the third section in index.html:
Open HTML
<section class="group-four" id="four"> <div class="summary-section"> <p>Name: </p> <p id="name-val"></p> <button type="button" class="edit-btn" id="name-edit"> <span>✎</span> <span>Edit</span> </button> </div> <div class="summary-section"> <p>ID Number: </p> <p id="id-val"></p> <button type="button" class="edit-btn" id="id-edit"> <span>✎</span> <span>Edit</span> </button> </div> <div class="summary-section"> <p>Email: </p> <p id="email-val"></p> <button type="button" class="edit-btn" id="email-edit"> <span>✎</span> <span>Edit</span> </button> </div> <div class="summary-section"> <p>Date of Birth: </p> <p id="bd-val"></p> <button type="button" class="edit-btn" id="bd-edit"> <span>✎</span> <span>Edit</span> </button> </div> <div class="summary-section"> <p>CV/Resume: </p> <p id="cv-val"></p> <button type="button" class="edit-btn" id="cv-edit"> <span>✎</span> <span>Edit</span> </button> </div> <div class="summary-section"> <p>Department: </p> <p id="dept-val"></p> <button type="button" class="edit-btn" id="dept-edit"> <span>✎</span> <span>Edit</span> </button> </div> <div class="summary-section"> <p>Skills: </p> <p id="skills-val"></p> <button type="button" class="edit-btn" id="skills-edit"> <span>✎</span> <span>Edit</span> </button> </div> <button id="btn" type="submit">Confirm and Submit</button> </section>
Then update the formStep in your Javascript to read:
const formSteps = ["one", "two", "three", "four"];
Finally, add the following classes to styles.css:
.summary-section display: flex; align-items: center; gap: 10px; .summary-section p:first-child width: 30%; flex-shrink: 0; border-right: 1px solid var(--secondary-color); .summary-section p:nth-child(2) width: 45%; flex-shrink: 0; padding-left: 10px; .edit-btn width: 25%; margin-left: auto; background-color: transparent; color: var(--primary-color); border: .7px solid var(--primary-color); border-radius: 5px; padding: 5px; .edit-btn:hover border: 2px solid var(--primary-color); font-weight: bolder; background-color: transparent;
Now, add the following to the top of the script.js file where the other consts are:
const nameVal = document.getElementById("name-val"); const idVal = document.getElementById("id-val"); const emailVal = document.getElementById("email-val"); const bdVal = document.getElementById("bd-val") const cvVal = document.getElementById("cv-val"); const deptVal = document.getElementById("dept-val"); const skillsVal = document.getElementById("skills-val"); const editButtons = "name-edit": 0, "id-edit": 0, "email-edit": 0, "bd-edit": 0, "cv-edit": 1, "dept-edit": 1, "skills-edit": 2 ;
Then add this function in scripts.js:
function updateSummaryValues() nameVal.textContent = nameInput.value; idVal.textContent = idNumInput.value; emailVal.textContent = emailInput.value; bdVal.textContent = birthdateInput.value; const fileName = documentInput.files[0]?.name; if (fileName) const extension = fileName.split(".").pop(); const baseName = fileName.split(".")[0]; const truncatedName = baseName.length > 10 ? baseName.substring(0, 10) + "..." : baseName; cvVal.textContent = `$truncatedName.$extension`; else cvVal.textContent = "No file selected"; deptVal.textContent = departmentInput.value; skillsVal.textContent = skillsInput.value || "No skills submitted"; }
This dynamically inserts the input values into the summary section of the form, truncates the file names, and offers a fallback text for the input that was not required.
Then update the updateStepVisibility() function to call the new function:
function updateStepVisibility() formSteps.forEach((step) => document.getElementById(step).style.display = "none"; ); document.getElementById(formSteps[currentStep]).style.display = "block"; stepInfo.textContent = `Step $currentStep + 1 of $formSteps.length`; if (currentStep === 3) updateSummaryValues(); navLeft.style.display = currentStep === 0 ? "none" : "block"; navRight.style.display = currentStep === formSteps.length - 1 ? "none" : "block";
Finally, add this to the DOMContentLoaded event listener:
Object.keys(editButtons).forEach((buttonId) => const button = document.getElementById(buttonId); button.addEventListener("click", (e) => currentStep = editButtons[buttonId]; updateStepVisibility(); ); );
Running the form, you should see that the summary section shows all the inputted values and allows the user to edit any before submitting the information:
And now, we can submit our form:
form.addEventListener("submit", (e) => e.preventDefault(); if (validateStep(2)) alert("Form submitted successfully!"); form.reset(); currentFormStep = 0; updateStepVisibility(); );
Our multi-step form now allows the user to edit and see all the information they provide before submitting it.
Accessibility tips
Making multi-step forms accessible starts with the basics: using semantic HTML. This is half the battle. It is closely followed by using appropriate form labels.
Other ways to make forms more accessible include giving enough room to elements that must be clicked on small screens and giving meaningful descriptions to the form navigation and progress indicators.
Offering feedback to the user is an important part of it; it’s not great to auto-dismiss user feedback after a certain amount of time but to allow the user to dismiss it themselves. Paying attention to contrast and font choice is important, too, as they both affect how readable your form is.
Let’s make the following adjustments to the markup for more technical accessibility:
Add aria-required="true" to all inputs except the skills one. This lets screen readers know the fields are required without relying on native validation.
Add role="alert" to the error spans. This helps screen readers know to give it importance when the input is in an error state.
Add role="status" aria-live="polite" to the .stepInfo. This will help screen readers understand that the step info keeps tabs on a state, and the aria-live being set to polite indicates that should the value change, it does not need to immediately announce it.
In the script file, replace the showError() and clearError() functions with the following:
function showError(input, message) const formControl = input.parentElement; const errorSpan = formControl.querySelector(".error-message"); input.classList.add("error"); input.setAttribute("aria-invalid", "true"); input.setAttribute("aria-describedby", errorSpan.id); errorSpan.textContent = message; function clearError(input) const formControl = input.parentElement; const errorSpan = formControl.querySelector(".error-message"); input.classList.remove("error"); input.removeAttribute("aria-invalid"); input.removeAttribute("aria-describedby"); errorSpan.textContent = "";
Here, we programmatically add and remove attributes that explicitly tie the input with its error span and show that it is in an invalid state.
Finally, let’s add focus on the first input of every section; add the following code to the end of the updateStepVisibility() function:
const currentStepElement = document.getElementById(formSteps[currentStep]); const firstInput = currentStepElement.querySelector( "input, select, textarea" ); if (firstInput) firstInput.focus();
And with that, the multi-step form is much more accessible.
Conclusion
There we go, a four-part multi-step form for a job application! As I said at the top of this article, there’s a lot to juggle — so much so that I wouldn’t fault you for looking for an out-of-the-box solution.
But if you have to hand-roll a multi-step form, hopefully now you see it’s not a death sentence. There’s a happy path that gets you there, complete with navigation and validation, without turning away from good, accessible practices.
And this is just how I approached it! Again, I took this on as a personal challenge to see how far I could get, and I’m pretty happy with it. But I’d love to know if you see additional opportunities to make this even more mindful of the user experience and considerate of accessibility.
References
Here are some relevant links I referred to when writing this article:
How to Structure a Web Form (MDN)
Multi-page Forms (W3C.org)
Create accessible forms (A11y Project)
3 notes · View notes
adellagreen · 6 months ago
Text
Tumblr media
10K notes · View notes
josegremarquez · 9 months ago
Text
CSS3: Revolucionando el Diseño Web en Alicante y la Comunidad Valenciana
Las Nuevas Reglas: CSS3 se Vuelve Loco CSS3 ha traĂ­do consigo una serie de propiedades y caracterĂ­sticas que han revolucionado la forma en que diseñamos y animamos nuestras pĂĄginas web. Estas nuevas herramientas nos permiten crear interfaces de usuario mĂĄs atractivas, interactivas y personalizadas. Exploremos algunas de las propiedades CSS3 mĂĄs populares y cĂłmo se estĂĄn utilizando en Alicante y

0 notes
mossy-box · 6 months ago
Text
I wish gay people were real
Tumblr media
6K notes · View notes
jackie-mae · 3 months ago
Note
https://www.tumblr.com/jackie-mae/775978232280383488/it-was-jelly-fish-wishes-who-commented-about-pure?source=share
Great now I need to see the gentle rocking in action because now I can't get it out of my head that when pure vanilla starts growing more attatched to him...instead of violently shacking the hat, he just starts gently rocking it to either get shadow milk to calm down or straight up fall asleep lol...
Main weapon against lies at first: rapid shaking!
Main weapon against lies once pure vanilla catches more feelings and affection for shadow milk: calm down rocking/nap time!
Can already slightly imagine:
Shadow milk: *lies or is getting fussy or something*
Pure vanilla: oop! Looks like someone needs nap time/oop, looks like somebody needs a nap.
Shadow milk: wait-
Tumblr media
You get me,
and to add to that I think PV instead of comforting Smilk like a baby but instead comforting him like his sheep (Completely oblivious to the well-known fact he treats his sheep like his own children) Is also just as fluffy and/or hilarious.
PV is so genuine and sweet it makes me want to vomit- me but also Smilk at some point
2K notes · View notes
sonadowcentral · 4 months ago
Text
Tumblr media Tumblr media Tumblr media Tumblr media
Teaser time cuz I’m so close to 30k on TikTok it’s insane
2K notes · View notes
pukefactory · 2 months ago
Note
hey.... I would like a Deceit Trio+ Reader who has N(Murder Drones😈) personality. Looks like a cinnamon roll, would kill you. (Gelp)
Tumblr media
⋆˖âș‧₊☜ DECEITFUL DROMEDA ☟₊‧âș˖⋆
⏟⋆ Summary: A Compilation of Headcannons Featuring The Deceit Trio X Reader Who Looks Like A Sweet Person, But Would Actually Kill Others
⏟⋆ Character(s): Black Sapphire Cookie (Cookie Run), Candy Apple Cookie (Cookie Run), Shadow Milk Cookie (Cookie Run)
⏟⋆ Genre: Headcannons, SFW
⏟⋆ Warning(s): None - Completely Safe!
Tumblr media
ïżœïżœ Shadow Milk Cookie figured it out almost instantly. Oh, how fun! A darling Cookie with honeyed words and a giggle sweet enough to rot teeth—yet, beneath that, a predator in powdered sugar. “Ahh, you’re just like me,” he croons, eyes glinting like shattered glass. He sees through your deception, but does he mind? Oh, no. If anything, he revels in it. How delightful it is to spin a tale where no one suspects the wolf dressed in frosting!
✶ “You’re dangerous. I like that,” Black Sapphire Cookie hums, voice laced with amusement as he idly spins his microphone between his fingers. He doesn’t care for virtue, honesty, or justice—he only cares about a good show. And you? You’re a walking plot twist, the kind that leaves audiences gasping. He’ll whisper rumors about you just to see how the world reacts. The best part? No one will believe a single one. Who would suspect such a sweetheart?
✶ Candy Apple Cookie adores you. “You’re like a candy apple with a razor blade inside! Hee hee! Just like me!” she sings, clinging to your arm. She drags you into her mischief, delighting in the way you smile so kindly before striking without mercy. “Betcha taste even sweeter with a little bit of deception!” she teases, licking her lips. She’s convinced you’re soulmates—twins in trickery.
✶ Shadow Milk watches you like a cat with a trapped bird. Oh, the duality of your nature intrigues him! A lie within a lie, a darling assassin wrapped in bows and pleasantries. “Tell me, little one,” he muses, leaning in with a lazy smirk, “do you ever tire of pretending?” He already knows the answer, but he loves watching you act. After all, a performer can’t stop the show.
✶ The best rumors are the ones no one believes. And you? You’re the juiciest secret Black Sapphire has ever uncovered. He enjoys dropping hints, little breadcrumbs leading nowhere. “Did you hear? The sweetest Cookie in town is hiding something deliciously wicked
” But of course, who would take Black Sapphire Cookie seriously? Lies are his trade, and the best lies are the ones dressed as jokes.
✶ “You don’t have to pretend around me, you know,” Candy Apple chirps, eyes twinkling with something unreadable. She’s peeled back the sugary layers of your act, and she loves what’s underneath. “It’s sooo fun watching you work! You’re like a surprise inside a candy box—except, oopsie! Someone might lose a finger!” She adores playing your shadow, watching you charm others before turning the tables.
✶ Your deception is artful, elegant, nearly as beautiful as Shadow Milk’s own. He finds himself testing you, toying with you, tempting you. How far will you go? How deep do your secrets run? “Oh, what a wonderful, wicked little thing you are,” he coos, voice dripping with amusement. You’re one of his favorite stories—so, so deliciously twisted.
✶ “You’re unpredictable. That makes you dangerous.” Black Sapphire’s usual script is built on knowing the outcome, but you? You throw his rhythm off just enough to make him interested. Black Sapphire Cookie enjoys control, but he’s willing to let you take the stage. Just once. Just to see how you’ll perform. And if you impress him? Oh, he might just rewrite the whole show for you.
✶ “Let’s do something fun,” Candy Apple whispers, dragging you along with gleeful eyes. “Something really sweet. Really sticky.” She giggles, twirling around you as if she doesn’t notice the way your eyes gleam when you smile. The two of you are chaos incarnate, a pretty little disaster wrapped in ribbons and grins. Oh, the world has no idea what’s coming.
✶ Shadow Milk’s hands rest over yours, leading you in a slow, taunting waltz. “I wonder,” he muses, his tone lilting, “when will the mask slip?” He dips you low, watching the way you refuse to falter. “Or
 do you even have a real face beneath it?” His laughter is soft, intoxicating, and yet—he knows. The best performers never reveal the truth.
563 notes · View notes
muttoncon · 1 month ago
Text
i feel like whitaker just
 doesn’t yell. like he can obviously yell when he’s scared or it’s like necessary but he just doesn’t really raise his voice. if he’s frustrated he just grits his teeth, sighs through his nose, and carries on.
santos — because she is the way she is (and i love her for it) — keeps trying to get him to break. it unnerves her how he just doesn’t seem to get mad at her. oh he definitely gets annoyed and exhausted, but he doesn’t raise his voice. when she won’t stop kicking him in the ass when he’s fixing the clogged sink drain he just
 lets her do it. if she’s being lazy and wants some ice cream from the freezer but doesn’t want to get up she only has to whine for a few minutes before he groans and gets it for her.
she’s trying to see his boundaries. to see how far she can push until he proves that he’s just like every other man she’s met and the fact that he won’t fold irks the shit out of her. idk i don’t think they’d ever really talk about it, but whitaker is a smart cookie and would probably figure out what she’s trying to do a few months in. he’d probably casually drop that his older brothers yelled at him all the time and then his poor mom would yell herself hoarse trying to get them to quit and oh here comes his dad yelling for everyone to shut the hell up. so he’s just always been quiet. quiet little dennis who once walked on a broken ankle for a few days because he didn’t wanna bother anyone.
and santos learns to live with that. to live comfortably with a guy who won’t scream at her or hurt her even when he gets mad at her. she learns to relax. to stop waiting for the other shoe to drop.
800 notes · View notes
dreamyblanket · 8 days ago
Note
Maybe- simulating cuddles with disembodied Shmilk? Like he wants affection before his body is ready
-🍋
Tumblr media
Something, something time goes by waaaayyy too quickly for mortals and he's technically a ghost so he can't even cuddle when he wants too :[
403 notes · View notes
mister-random · 3 months ago
Note
could you draw Shadow with Maria? please
Tumblr media Tumblr media Tumblr media
I didn't feel like painting, so here u got some sketches, hope it's enough 😬👍
543 notes · View notes
sophfandoms53 · 11 months ago
Text
Tumblr media Tumblr media Tumblr media
Shadow fans,,,, how are we possibly still alive right now like this is absolutely insane
1K notes · View notes
jcmarchi · 1 year ago
Text
Weekly News for Designers № 727 - Fixing CLS Problems, CSS One-Line Upgrades, Future Roles for Designers
New Post has been published on https://thedigitalinsider.com/weekly-news-for-designers-%e2%84%96-727-fixing-cls-problems-css-one-line-upgrades-future-roles-for-designers/
Weekly News for Designers № 727 - Fixing CLS Problems, CSS One-Line Upgrades, Future Roles for Designers
Happy Birthday, Macintosh Forty years ago, Apple introduced the world to the Macintosh computer.
Free Instagram Story Templates A collection of Instagram Story templates for Photoshop, Figma, Sketch, After Effects, Premiere Pro, and Final Cut Pro.
12 Modern CSS One-Line Upgrades Learn about the CSS properties to enhance your projects, reduce technical debt, eliminate JavaScript, and improve the UX.
The Diagram that Shows the Value of Great UX
Fading Content Using Transparent Gradients in CSS Here are two methods for achieving text content fading with CSS. One uses mask-image and the other background-clip.
Top Logo Stinger Premiere Pro Templates We share a collection of logo stinger templates for Premiere Pro that stand out with their style, functionality, and ease of use.
Five Future Roles for Designers Jorge Arango shares five possible future careers for designers in our now AI-driven world.
CSS Blurry Shimmer Effect Learn how to create a CSS blurring effect, but not with box-shadow.
The CSS Snippets Every Developer Should Know Discover the CSS snippets that every front-end developer should know about in 2024.
What’s the Environmental Impact of Your Website? Eric examines the relationship between the web and the planet and shows how to measure your website’s impact.
Git and GitHub Essentials If you’re new to Git or GitHub, this extensive beginner’s guide of the most common commands is for you.
Fixing Cumulative Layout Shift Problems
The Most Underused CSS Media Queries: hover & any-hover Learn how to use the hover and any-hover media queries for responsive design and better experiences on all devices.
Improve Your Logo Design Skills Melinda Livsey shares how she improved her logo design skills by studying the work of Paul Rand and Saul Bass.
2 notes · View notes
disgracedghostprincess · 2 months ago
Text
Tumblr media Tumblr media Tumblr media Tumblr media
HERES THE PART 2 @cyucya
It was too long to put into the last part but I wanted to share anyways cause gosh I love them their so silly
Continuation of this comic with Skylar-Chilli
Sonic Masterpost
369 notes · View notes
bfleuterart · 4 months ago
Text
Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media
Just posting my upcycled sculptures again.
550 notes · View notes
ecto-stone · 6 months ago
Text
Tumblr media
First ship of the day Ê•â€ąáŽ„â€ąÊ” Teddy Ghost đŸ‘»
920 notes · View notes