Vue.js course Β· Module 2: Template Syntax
Vue Directives
In this lesson4
Directives are special attributes starting with v-. Each directive is like an instruction for the Mars base control system.
The mustaches from the previous lesson have two gaps. They cannot reach attributes, so a sensor image URL or a disabled button cannot depend on the data. And they cannot hear the operator: clicking the hologram triggers nothing. Directives close both gaps. Their structure is always the same: a name after the v- prefix, an argument after a colon, modifiers after dots and an expression in quotes. In v-on:submit.prevent="onSubmit" the name is on, the argument is submit, the modifier is prevent, and the value is the onSubmit function.
v-bind - Attribute Binding
v-bind keeps an attribute in sync with the data: after the colon you give the attribute name, and in quotes an expression. Since this directive appears all the time, it has a shorthand - just the colon. Here is a panel template with four typical cases:
1<template>
2 <div class="control-panel" style="background: #0a0e27; padding: 1rem;">
3 <!-- Full syntax -->
4 <img v-bind:src="sensorImage" v-bind:alt="sensorName">
5
6 <!-- Shorthand syntax (recommended) -->
7 <img :src="sensorImage" :alt="sensorName">
8
9 <!-- Dynamic attributes -->
10 <a :href="documentationUrl" :class="linkClass">Documentation</a>
11
12 <!-- Boolean attributes -->
13 <button :disabled="isProcessing" class="btn-neon">Launch</button>
14 </div>
15</template>Both images produce identical HTML - the shorthand changes nothing but the length of the code. The quotes hold an expression, not text: :alt="sensorName" inserts the variable's value, while a plain alt="sensorName" would write the word sensorName literally. The boolean attribute disabled appears when the value is truthy and disappears for false. When a binding evaluates to null or undefined, Vue removes the attribute entirely.
The data for these bindings are ordinary refs from the script section - nothing new compared with module 1:
1<script setup>
2import { ref } from 'vue'
3
4const sensorImage = ref('/images/oxygen-sensor.png')
5const sensorName = ref('Oxygen Sensor MK-7')
6const documentationUrl = ref('https://nova-lab.mars/docs')
7const linkClass = ref('neon-link')
8const isProcessing = ref(false)
9</script>Setting isProcessing.value = true is enough for the button to get the disabled attribute - you do not touch the template. The button's look comes from ordinary CSS, because v-bind does not change how styles work:
1<style>
2.btn-neon {
3 background: transparent;
4 border: 2px solid #00ff88;
5 color: #00ff88;
6 padding: 0.5rem 1rem;
7 cursor: pointer;
8}
9.btn-neon:hover {
10 background: #00ff8822;
11 box-shadow: 0 0 10px #00ff88;
12}
13</style>There is not a single Vue feature in the style section. My advice: always write the : shorthand - in a dense template you immediately see which attributes are alive. Since Vue 3.4 an even shorter form works: when the attribute and the variable share a name, :id means the same as :id="id".
v-on - Event Handling
v-on attaches a function to a browser event; its shorthand is the at sign @. The value can be a function name or a short statement, for example a call with an argument. The special $event variable passes the event object when you call the function yourself:
1<template>
2 <div class="reactor-controls" style="background: #0a0e27; color: #00b4d8;">
3 <!-- Full syntax -->
4 <button v-on:click="handleActivation">Activate</button>
5
6 <!-- Shorthand syntax (recommended) -->
7 <button @click="handleActivation" class="btn-reactor">Activate Reactor</button>
8
9 <!-- Inline handler -->
10 <button @click="powerLevel++">Increase Power</button>
11
12 <!-- With argument -->
13 <button @click="sendSignal('ALPHA-BASE')">Send Signal</button>
14
15 <!-- With event object -->
16 <button @click="handleActivation($event)">Event Info</button>
17 </div>
18</template>The compiler recognizes what you gave it: the bare name handleActivation means Vue calls the function and passes the native event as its only argument. A form with parentheses is a statement, so the event has to be passed explicitly through $event. There is no .value in powerLevel++, because refs are unwrapped in the template.
The handler functions live in the script, where .value applies again:
1<script setup>
2import { ref } from 'vue'
3
4const powerLevel = ref(0)
5
6function handleActivation(event) {
7 console.log('Reactor activated!', event)
8}
9
10function sendSignal(baseCode) {
11 alert(`Sending signal to: ${baseCode}`)
12}
13</script>The button is still an ordinary <button> - v-on only adds an event listener to it, just as addEventListener would.
Event Modifiers
Calls like event.preventDefault() or event.stopPropagation() clutter functions with DOM details. A modifier added after a dot takes care of that in the template. The cheat sheet below shows only the opening tags:
1<template>
2 <div class="mission-control">
3 <!-- Prevent default -->
4 <form @submit.prevent="onSubmit" class="mission-form">
5
6 <!-- Stop propagation -->
7 <div @click.stop="onClick" class="stop-zone">
8
9 <!-- Only once -->
10 <button @click.once="launchSequence" class="btn-launch">Start Sequence</button>
11
12 <!-- Capture mode -->
13 <div @click.capture="onCapture" class="capture-zone">
14
15 <!-- Self only -->
16 <div @click.self="onSelf" class="self-zone">
17
18 <!-- Combination -->
19 <a @click.stop.prevent="onClick" class="safe-link">
20 </div>
21</template>.prevent calls event.preventDefault(), so the form does not reload the page. .stop calls event.stopPropagation(), and the event does not reach the parents. .once lets the handler fire at most once, .capture handles the event in the capture phase, before it reaches the child, and .self reacts only when the element itself was clicked. When the event arrives, Vue applies the modifiers first and only then calls your function. In a chain the order matters: @click.prevent.self blocks the default action for the children too, while @click.self.prevent does it only for the element itself.
Key Modifiers
The keyup and keydown events have modifiers too. Vue knows the aliases .enter, .tab, .delete (Delete and Backspace), .esc, .space and the arrows, and the system keys are .ctrl, .alt, .shift and .meta. The functions in the example are defined in the script:
1<template>
2 <input @keyup.enter="sendCommand" placeholder="Command for the station">
3 <input @keyup.esc="clearCommand" placeholder="Esc clears the field">
4 <button @click.ctrl="emergencyStop">Ctrl + click: emergency stop</button>
5</template>sendCommand runs only after Enter, clearCommand only after Escape, and the emergency stop requires Ctrl to be held down. Other keys trigger nothing - without a single if checking event.key. In the next lesson you will see that :class and :style are special kinds of v-bind that accept objects and arrays.
Remember: the colon connects an attribute to data, the at sign connects an event to a function, and the dot adds a safety protocol to the command.
Code for this lesson: App.vue
1<script setup>
2import { ref } from 'vue'
3
4const stationStatus = ref('operational')
5const powerLevel = ref(85)
6const alertLevel = ref('low')
7const showDetails = ref(false)
8const logoUrl = ref('https://placehold.co/100/00ff88/0a0e27?text=NOVA')
9
10const systemMessages = ref([
11 { id: 1, text: 'Life support systems: ONLINE', type: 'success' },
12 { id: 2, text: 'Energy level: STABLE', type: 'info' },
13 { id: 3, text: 'Communication with Earth: ACTIVE', type: 'success' }
14])
15
16function toggleDetails() {
17 showDetails.value = !showDetails.value
18}
19
20function increasePower() {
21 if (powerLevel.value < 100) {
22 powerLevel.value += 5
23 }
24}
25
26function decreasePower() {
27 if (powerLevel.value > 0) {
28 powerLevel.value -= 5
29 }
30}
31</script>
32
33<template>
34 <div class="control-panel">
35 <div class="station-header">
36 <!-- v-bind for src and alt attributes -->
37 <img :src="logoUrl" :alt="'NOVA LAB Logo'" class="logo">
38 <h1>NOVA Research Station</h1>
39 </div>
40
41 <div class="power-control">
42 <h2>Energy Control</h2>
43 <!-- v-bind for class and style -->
44 <div
45 class="power-bar"
46 :class="{ 'critical': powerLevel < 30, 'optimal': powerLevel >= 70 }"
47 :style="{ width: powerLevel + '%' }"
48 >
49 {{ powerLevel }}%
50 </div>
51
52 <!-- v-on (@) for event handling -->
53 <div class="controls">
54 <button @click="decreasePower" class="btn btn-danger">-5%</button>
55 <button @click="increasePower" class="btn btn-success">+5%</button>
56 </div>
57 </div>
58
59 <div class="status-section">
60 <h2>Systems Status</h2>
61 <button @click="toggleDetails" class="btn btn-info">
62 {{ showDetails ? 'Hide details' : 'Show details' }}
63 </button>
64
65 <div v-show="showDetails" class="messages">
66 <div
67 v-for="msg in systemMessages"
68 :key="msg.id"
69 :class="['message', 'message-' + msg.type]"
70 >
71 {{ msg.text }}
72 </div>
73 </div>
74 </div>
75
76 <div class="info-box">
77 <h3>Vue.js directives</h3>
78 <ul>
79 <li><strong>v-bind</strong> (shorthand :) - attribute binding</li>
80 <li><strong>v-on</strong> (shorthand @) - event handling</li>
81 <li><strong>:class</strong> - dynamic CSS classes</li>
82 <li><strong>:style</strong> - dynamic inline styles</li>
83 </ul>
84 </div>
85 </div>
86</template>
87
88<style scoped>
89.control-panel {
90 background: linear-gradient(135deg, #0a0e27 0%, #1a1e3f 100%);
91 min-height: 100vh;
92 padding: 2rem;
93 color: #ffffff;
94 font-family: 'Courier New', monospace;
95}
96
97.station-header {
98 text-align: center;
99 margin-bottom: 2rem;
100}
101
102.logo {
103 width: 100px;
104 height: 100px;
105 border-radius: 50%;
106 border: 3px solid #00ff88;
107 box-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
108}
109
110h1, h2, h3 {
111 color: #00ff88;
112 text-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
113}
114
115.power-control {
116 background: rgba(0, 255, 136, 0.1);
117 border: 2px solid #00ff88;
118 border-radius: 10px;
119 padding: 1.5rem;
120 margin-bottom: 2rem;
121}
122
123.power-bar {
124 height: 40px;
125 background: linear-gradient(90deg, #00ff88, #00b4d8);
126 border-radius: 20px;
127 display: flex;
128 align-items: center;
129 justify-content: center;
130 font-weight: bold;
131 transition: all 0.3s ease;
132 margin: 1rem 0;
133}
134
135.power-bar.critical {
136 background: linear-gradient(90deg, #ff0055, #ff6600);
137 animation: pulse 1s infinite;
138}
139
140.power-bar.optimal {
141 box-shadow: 0 0 20px rgba(0, 255, 136, 0.8);
142}
143
144@keyframes pulse {
145 0%, 100% { opacity: 1; }
146 50% { opacity: 0.7; }
147}
148
149.controls {
150 display: flex;
151 gap: 1rem;
152 justify-content: center;
153 margin-top: 1rem;
154}
155
156.btn {
157 padding: 0.75rem 1.5rem;
158 border: none;
159 border-radius: 5px;
160 font-size: 1rem;
161 font-weight: bold;
162 cursor: pointer;
163 transition: all 0.3s ease;
164 font-family: 'Courier New', monospace;
165}
166
167.btn-success {
168 background: #00ff88;
169 color: #0a0e27;
170}
171
172.btn-danger {
173 background: #ff0055;
174 color: #ffffff;
175}
176
177.btn-info {
178 background: #00b4d8;
179 color: #ffffff;
180}
181
182.btn:hover {
183 transform: scale(1.05);
184 box-shadow: 0 5px 15px rgba(0, 255, 136, 0.4);
185}
186
187.status-section {
188 background: rgba(0, 180, 216, 0.1);
189 border: 2px solid #00b4d8;
190 border-radius: 10px;
191 padding: 1.5rem;
192 margin-bottom: 2rem;
193}
194
195.messages {
196 margin-top: 1rem;
197}
198
199.message {
200 padding: 1rem;
201 margin: 0.5rem 0;
202 border-radius: 5px;
203 border-left: 4px solid;
204}
205
206.message-success {
207 background: rgba(0, 255, 136, 0.2);
208 border-color: #00ff88;
209}
210
211.message-info {
212 background: rgba(0, 180, 216, 0.2);
213 border-color: #00b4d8;
214}
215
216.info-box {
217 background: rgba(255, 255, 255, 0.05);
218 border: 2px solid #00ff88;
219 border-radius: 10px;
220 padding: 1.5rem;
221}
222
223.info-box ul {
224 list-style: none;
225 padding: 0;
226}
227
228.info-box li {
229 padding: 0.5rem 0;
230 color: #ffffff;
231}
232
233.info-box strong {
234 color: #00b4d8;
235}
236</style>The second editor is a modifiers lab: submit its form with .prevent, click the buttons with .stop and .once, and try Enter, Escape and the Ctrl+S shortcut in the text fields.
Code for this lesson: App.vue
1<script setup>
2import { ref } from 'vue'
3
4const commandInput = ref('')
5const searchQuery = ref('')
6const formData = ref({
7 mission: '',
8 priority: 'medium',
9 description: ''
10})
11
12const logs = ref([])
13const systemMessages = ref([
14 { id: 1, text: 'System initialized' },
15 { id: 2, text: 'Connection established with Mars Base' },
16 { id: 3, text: 'All systems operational' }
17])
18let nextMessageId = 4
19
20function handleCommand(event) {
21 event.preventDefault()
22 if (commandInput.value.trim()) {
23 logs.value.unshift({
24 id: Date.now(),
25 command: commandInput.value,
26 timestamp: new Date().toLocaleTimeString()
27 })
28 commandInput.value = ''
29 }
30}
31
32function preventRefresh(event) {
33 event.preventDefault()
34 alert('Form was stopped by event.preventDefault()')
35}
36
37function stopPropagation(event) {
38 event.stopPropagation()
39 systemMessages.value.unshift({ id: nextMessageId++, text: 'Clicked button - propagation stopped' })
40}
41
42function handleContainerClick() {
43 systemMessages.value.unshift({ id: nextMessageId++, text: 'Container clicked' })
44}
45
46function runOnce() {
47 systemMessages.value.unshift({ id: nextMessageId++, text: 'This handler runs only once!' })
48}
49
50function handleEnter() {
51 systemMessages.value.unshift({ id: nextMessageId++, text: `Search executed: ${searchQuery.value}` })
52 searchQuery.value = ''
53}
54
55function handleEscape() {
56 searchQuery.value = ''
57 systemMessages.value.unshift({ id: nextMessageId++, text: 'Search cleared with ESC' })
58}
59
60function handleCtrlS(event) {
61 event.preventDefault()
62 systemMessages.value.unshift({ id: nextMessageId++, text: 'Mission saved (Ctrl+S)' })
63}
64
65function submitMission(event) {
66 event.preventDefault()
67 logs.value.unshift({
68 id: Date.now(),
69 command: `Mission: ${formData.value.mission} | Priority: ${formData.value.priority}`,
70 timestamp: new Date().toLocaleTimeString()
71 })
72 formData.value.mission = ''
73 formData.value.description = ''
74}
75</script>
76
77<template>
78 <div class="modifiers-lab">
79 <h1>Modifiers Laboratory - NOVA LAB</h1>
80
81 <!-- Event Modifiers Section -->
82 <section class="lab-section">
83 <h2>Event Modifiers</h2>
84
85 <!-- .prevent modifier -->
86 <div class="demo-card">
87 <h3>.prevent - Preventing default action</h3>
88 <form @submit.prevent="preventRefresh" class="demo-form">
89 <input
90 type="text"
91 placeholder="This form will not refresh the page"
92 class="input-field"
93 >
94 <button type="submit" class="btn btn-primary">
95 Submit (preventDefault)
96 </button>
97 </form>
98 <code class="code-hint">@submit.prevent="preventRefresh"</code>
99 </div>
100
101 <!-- .stop modifier -->
102 <div class="demo-card">
103 <h3>.stop - Stopping event propagation</h3>
104 <div @click="handleContainerClick" class="click-container">
105 Click the container (event propagates)
106 <button @click.stop="stopPropagation" class="btn btn-warning">
107 Click the button (stop propagation)
108 </button>
109 </div>
110 <code class="code-hint">@click.stop="stopPropagation"</code>
111 </div>
112
113 <!-- .once modifier -->
114 <div class="demo-card">
115 <h3>.once - Handler runs only once</h3>
116 <button @click.once="runOnce" class="btn btn-success">
117 Click me (works only once)
118 </button>
119 <code class="code-hint">@click.once="runOnce"</code>
120 </div>
121
122 <!-- Multiple modifiers -->
123 <div class="demo-card">
124 <h3>Combining modifiers</h3>
125 <form @submit.prevent.stop="handleCommand" class="demo-form">
126 <input
127 v-model="commandInput"
128 type="text"
129 placeholder="Enter command..."
130 class="input-field"
131 >
132 <button type="submit" class="btn btn-primary">
133 Execute Command
134 </button>
135 </form>
136 <code class="code-hint">@submit.prevent.stop="handleCommand"</code>
137 </div>
138 </section>
139
140 <!-- Key Modifiers Section -->
141 <section class="lab-section">
142 <h2>Key Modifiers</h2>
143
144 <!-- .enter modifier -->
145 <div class="demo-card">
146 <h3>.enter - Detects the Enter key</h3>
147 <input
148 v-model="searchQuery"
149 @keyup.enter="handleEnter"
150 type="text"
151 placeholder="Type and press Enter..."
152 class="input-field"
153 >
154 <code class="code-hint">@keyup.enter="handleEnter"</code>
155 </div>
156
157 <!-- .esc modifier -->
158 <div class="demo-card">
159 <h3>.esc - Detects the Escape key</h3>
160 <input
161 v-model="searchQuery"
162 @keyup.esc="handleEscape"
163 type="text"
164 placeholder="Type and press ESC to clear..."
165 class="input-field"
166 >
167 <code class="code-hint">@keyup.esc="handleEscape"</code>
168 </div>
169
170 <!-- System modifier keys -->
171 <div class="demo-card">
172 <h3>.ctrl + key - Key combinations</h3>
173 <textarea
174 v-model="formData.description"
175 @keydown.ctrl.s="handleCtrlS"
176 placeholder="Press Ctrl+S to save..."
177 class="textarea-field"
178 rows="4"
179 ></textarea>
180 <code class="code-hint">@keydown.ctrl.s="handleCtrlS"</code>
181 </div>
182
183 <!-- Multiple key modifiers -->
184 <div class="demo-card">
185 <h3>Complete form with modifiers</h3>
186 <form @submit.prevent="submitMission" class="mission-form">
187 <input
188 v-model="formData.mission"
189 type="text"
190 placeholder="Mission name (Enter to submit)..."
191 class="input-field"
192 >
193 <select v-model="formData.priority" class="select-field">
194 <option value="low">Low Priority</option>
195 <option value="medium">Medium Priority</option>
196 <option value="high">High Priority</option>
197 </select>
198 <button type="submit" class="btn btn-primary">
199 Submit Mission
200 </button>
201 </form>
202 </div>
203 </section>
204
205 <!-- System Messages -->
206 <section class="lab-section">
207 <h2>System Messages</h2>
208 <div class="messages-container">
209 <div
210 v-for="msg in systemMessages.slice(0, 10)"
211 :key="msg.id"
212 class="message-item"
213 >
214 <span class="message-time">{{ new Date().toLocaleTimeString() }}</span>
215 <span class="message-text">{{ msg.text }}</span>
216 </div>
217 </div>
218 </section>
219
220 <!-- Command Logs -->
221 <section class="lab-section" v-if="logs.length > 0">
222 <h2>Command Logs</h2>
223 <div class="logs-container">
224 <div
225 v-for="log in logs"
226 :key="log.id"
227 class="log-item"
228 >
229 <span class="log-time">{{ log.timestamp }}</span>
230 <span class="log-command">{{ log.command }}</span>
231 </div>
232 </div>
233 </section>
234
235 <!-- Info Box -->
236 <div class="info-box">
237 <h3>Event & Key Modifiers in Vue</h3>
238 <div class="info-grid">
239 <div>
240 <h4>Event Modifiers:</h4>
241 <ul>
242 <li><strong>.prevent</strong> - event.preventDefault()</li>
243 <li><strong>.stop</strong> - event.stopPropagation()</li>
244 <li><strong>.once</strong> - handler only once</li>
245 <li><strong>.capture</strong> - capture mode</li>
246 <li><strong>.self</strong> - only if target === currentTarget</li>
247 <li><strong>.passive</strong> - { passive: true }</li>
248 </ul>
249 </div>
250 <div>
251 <h4>Key Modifiers:</h4>
252 <ul>
253 <li><strong>.enter</strong> - Enter key</li>
254 <li><strong>.tab</strong> - Tab key</li>
255 <li><strong>.delete</strong> - Delete/Backspace</li>
256 <li><strong>.esc</strong> - Escape key</li>
257 <li><strong>.space</strong> - Space key</li>
258 <li><strong>.up/.down/.left/.right</strong> - Arrow keys</li>
259 <li><strong>.ctrl/.alt/.shift/.meta</strong> - System keys</li>
260 </ul>
261 </div>
262 </div>
263 </div>
264 </div>
265</template>
266
267<style scoped>
268.modifiers-lab {
269 background: linear-gradient(135deg, #0a0e27 0%, #1a1e3f 100%);
270 min-height: 100vh;
271 padding: 2rem;
272 color: #ffffff;
273 font-family: 'Courier New', monospace;
274}
275
276h1 {
277 text-align: center;
278 color: #00ff88;
279 text-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
280 margin-bottom: 3rem;
281}
282
283h2 {
284 color: #00b4d8;
285 margin-bottom: 1.5rem;
286 border-bottom: 2px solid #00b4d8;
287 padding-bottom: 0.5rem;
288}
289
290h3 {
291 color: #00ff88;
292 font-size: 1.1rem;
293 margin-bottom: 1rem;
294}
295
296.lab-section {
297 background: rgba(0, 255, 136, 0.05);
298 border: 2px solid #00ff88;
299 border-radius: 10px;
300 padding: 2rem;
301 margin-bottom: 2rem;
302}
303
304.demo-card {
305 background: rgba(0, 180, 216, 0.1);
306 border: 2px solid #00b4d8;
307 border-radius: 8px;
308 padding: 1.5rem;
309 margin-bottom: 1.5rem;
310}
311
312.demo-form {
313 display: flex;
314 gap: 1rem;
315 margin-bottom: 1rem;
316 flex-wrap: wrap;
317}
318
319.mission-form {
320 display: flex;
321 flex-direction: column;
322 gap: 1rem;
323}
324
325.input-field, .select-field, .textarea-field {
326 background: rgba(0, 0, 0, 0.3);
327 border: 2px solid #00b4d8;
328 border-radius: 5px;
329 padding: 0.75rem;
330 color: #ffffff;
331 font-family: 'Courier New', monospace;
332 font-size: 1rem;
333 flex: 1;
334 min-width: 200px;
335}
336
337.input-field:focus, .select-field:focus, .textarea-field:focus {
338 outline: none;
339 border-color: #00ff88;
340 box-shadow: 0 0 10px rgba(0, 255, 136, 0.3);
341}
342
343.input-field::placeholder, .textarea-field::placeholder {
344 color: rgba(255, 255, 255, 0.5);
345}
346
347.btn {
348 padding: 0.75rem 1.5rem;
349 border: none;
350 border-radius: 5px;
351 font-size: 1rem;
352 font-weight: bold;
353 cursor: pointer;
354 transition: all 0.3s ease;
355 font-family: 'Courier New', monospace;
356}
357
358.btn-primary {
359 background: #00ff88;
360 color: #0a0e27;
361}
362
363.btn-warning {
364 background: #ffb400;
365 color: #0a0e27;
366}
367
368.btn-success {
369 background: #00b4d8;
370 color: #ffffff;
371}
372
373.btn:hover {
374 transform: scale(1.05);
375 box-shadow: 0 5px 15px rgba(0, 255, 136, 0.4);
376}
377
378.click-container {
379 background: rgba(255, 180, 0, 0.1);
380 border: 2px dashed #ffb400;
381 border-radius: 5px;
382 padding: 2rem;
383 text-align: center;
384 cursor: pointer;
385 transition: all 0.3s ease;
386 margin-bottom: 1rem;
387}
388
389.click-container:hover {
390 background: rgba(255, 180, 0, 0.2);
391}
392
393.code-hint {
394 display: block;
395 background: rgba(0, 0, 0, 0.5);
396 border-left: 4px solid #00ff88;
397 padding: 0.75rem;
398 border-radius: 5px;
399 color: #00ff88;
400 font-family: 'Courier New', monospace;
401 font-size: 0.9rem;
402}
403
404.messages-container, .logs-container {
405 background: rgba(0, 0, 0, 0.3);
406 border-radius: 8px;
407 padding: 1rem;
408 max-height: 300px;
409 overflow-y: auto;
410}
411
412.message-item, .log-item {
413 padding: 0.75rem;
414 background: rgba(0, 180, 216, 0.1);
415 border-left: 4px solid #00b4d8;
416 border-radius: 5px;
417 margin-bottom: 0.5rem;
418 display: flex;
419 gap: 1rem;
420 align-items: center;
421}
422
423.message-time, .log-time {
424 color: #ffb400;
425 font-size: 0.9rem;
426 min-width: 100px;
427}
428
429.message-text, .log-command {
430 color: #ffffff;
431 flex: 1;
432}
433
434.info-box {
435 background: rgba(0, 255, 136, 0.05);
436 border: 2px solid #00ff88;
437 border-radius: 10px;
438 padding: 2rem;
439}
440
441.info-box h3 {
442 margin-top: 0;
443 margin-bottom: 1.5rem;
444}
445
446.info-grid {
447 display: grid;
448 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
449 gap: 2rem;
450}
451
452.info-box h4 {
453 color: #00b4d8;
454 margin-bottom: 1rem;
455}
456
457.info-box ul {
458 list-style: none;
459 padding: 0;
460}
461
462.info-box li {
463 padding: 0.5rem 0;
464 color: #ffffff;
465 border-left: 3px solid #00b4d8;
466 padding-left: 1rem;
467 margin: 0.5rem 0;
468}
469
470.info-box strong {
471 color: #00ff88;
472}
473</style>Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What is the v-bind directive used for?
2. What is the shorthand for v-bind:href?
3. What is v-on used for?
4. What does <img :src> without a value mean when a src variable exists in <script setup>?
5. What does .prevent do in @submit.prevent?
6. What does the .stop modifier do in v-on?
Hands-on tasks in the game
- Code editor
Use v-bind (shorthand :) to assign dynamic src, alt, href, and disabled attributes.
- Click in order
Arrange the click handling syntax with an argument:
- Code editor
Use v-on (or its @ shorthand) to handle the click, submit and keyup events.
- Horizontal ordering
Arrange the correct v-bind syntax for a dynamic src attribute:
- Horizontal ordering
Arrange the correct v-on syntax for handling a click:
- Vertical ordering
Order the steps of handling a click event in Vue:
- Code editor
Use the .prevent, .stop, and .once modifiers in event handling.