π§ What is Relation, its attributes, tuple?
A Relation is like a table in a relational database.
It has rows and columns.
It stores data about a particular entity (like Students, Products, etc.).
π§ Think of it as:
β‘οΈ Relation = Table
Attributes are the columns in a relation.
Each attribute defines a property of the entity.
Every column has a name and data type (like text, number, date, etc.).
π Example Attributes in a Students table:
π StudentID, π€ Name, π Age, π Major
π§ Think of it as:
β‘οΈ Attributes = Columns (Fields)
A Tuple is a single row in a table.
It represents one complete record.
Contains values for all attributes.
π Example Tuple:
(π 101, π€ "Alice", π 20, π "Computer Science")
π§ Think of it as:
β‘οΈ Tuple = Row (Record)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Relation, Attribute & Tuple</title>
<style>
.infographic-reltable {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--display-font: Georgia, 'Iowan Old Style', serif;
--body-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--mono-font: 'SF Mono', Consolas, monospace;
background: var(--paper);
color: var(--ink);
font-family: var(--body-font);
max-width: 1000px;
margin: 0 auto;
box-sizing: border-box;
overflow-x: hidden;
}
.infographic-reltable[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.infographic-reltable * { box-sizing: border-box; }
.rt-head { text-align: center; margin-bottom: 0.25rem; }
.rt-head h2 {
font-family: var(--display-font);
font-size: 2rem;
margin: 0 0 0.5rem;
letter-spacing: -0.01em;
}
.rt-head p {
color: var(--ink-soft);
font-size: 1.02rem;
margin: 0 auto;
max-width: 560px;
line-height: 1.5;
}
.rt-body {
display: grid;
grid-template-columns: 1fr 280px;
gap: 2rem;
align-items: start;
margin-top: 2.25rem;
min-width: 0;
}
.rt-body > div { min-width: 0; }
@media (max-width: 760px) {
.rt-body { grid-template-columns: 1fr; }
}
.rt-controls {
display: flex;
justify-content: center;
gap: 0.6rem;
margin: 1.5rem 0 0;
}
.rt-btn {
font: inherit;
font-size: 0.88rem;
background: var(--paper-alt);
border: 1px solid var(--line);
border-radius: 8px;
padding: 0.55rem 1.3rem;
cursor: pointer;
color: var(--ink);
transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.rt-btn:hover { border-color: var(--accent); }
.rt-btn.is-active { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.rt-frame {
background: var(--paper-alt);
border: 2px dashed var(--line);
border-radius: 12px;
padding: 1.75rem 1.25rem 1.25rem;
transition: border-color 0.15s, background 0.15s;
min-width: 0;
}
.rt-frame.is-active {
border-color: var(--accent);
border-style: solid;
background: var(--accent-soft);
}
.rt-frame-label {
text-align: center;
font-size: 0.8rem;
color: var(--ink-soft);
text-transform: uppercase;
letter-spacing: 0.05em;
margin: 0 0 0.75rem;
}
table.rt-table {
width: 100%;
min-width: 480px;
border-collapse: collapse;
background: var(--paper);
border-radius: 8px;
overflow: hidden;
font-size: 0.95rem;
}
.rt-table-scroll {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
border-radius: 8px;
}
table.rt-table th, table.rt-table td {
border: 1px solid var(--line);
padding: 0.65rem 0.8rem;
text-align: left;
white-space: nowrap;
}
table.rt-table thead th {
background: var(--paper-alt);
font-weight: 700;
transition: background 0.15s, color 0.15s;
}
table.rt-table thead th.is-active {
background: var(--accent);
color: #fff;
}
table.rt-table tbody tr {
transition: background 0.15s;
}
table.rt-table tbody tr.is-active td {
background: var(--accent-soft);
}
table.rt-table td.is-col-active,
table.rt-table th.is-active {
background: var(--accent-soft);
}
table.rt-table thead th.is-active { background: var(--accent); color: #fff; }
.rt-panel {
background: var(--paper-alt);
border: 1px solid var(--line);
border-radius: 10px;
padding: 1.1rem 1.2rem;
}
.rt-panel .rt-panel-title {
font-family: var(--display-font);
font-size: 1.15rem;
margin: 0 0 0.2rem;
color: var(--accent);
}
.rt-panel .rt-panel-role {
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--ink-soft);
margin: 0 0 0.85rem;
}
.rt-panel .rt-panel-desc {
font-size: 0.9rem;
color: var(--ink-soft);
line-height: 1.55;
margin: 0;
}
.rt-panel .rt-hint {
font-size: 0.9rem;
color: var(--ink-soft);
line-height: 1.55;
}
@media (max-width: 480px) {
.rt-frame { padding: 1.25rem 0.75rem 0.9rem; }
.rt-controls { flex-wrap: wrap; }
.rt-btn { flex: 1 1 auto; padding: 0.55rem 0.8rem; }
table.rt-table { font-size: 0.85rem; }
table.rt-table th, table.rt-table td { padding: 0.5rem 0.6rem; }
}
@media (prefers-reduced-motion: reduce) {
.infographic-reltable *, .infographic-reltable *::before, .infographic-reltable *::after {
transition-duration: 0.001ms !important;
animation-duration: 0.001ms !important;
}
}
</style>
</head>
<body>
<div class="infographic-reltable" id="reltable">
<div class="rt-head">
<h2>Relation, Attribute & Tuple</h2>
<p>A relational table has names for its whole self, its columns, and its rows. Click each part below to see which is which.</p>
</div>
<div class="rt-controls">
<button class="rt-btn" id="rtBtnRelation" type="button">Relation</button>
<button class="rt-btn" id="rtBtnAttribute" type="button">Attribute</button>
<button class="rt-btn" id="rtBtnTuple" type="button">Tuple</button>
</div>
<div class="rt-body">
<div>
<div class="rt-frame" id="rtFrame">
<p class="rt-frame-label">Students table</p>
<div class="rt-table-scroll">
<table class="rt-table" id="rtTable">
<thead>
<tr>
<th data-col="0">π StudentID</th>
<th data-col="1">π€ Name</th>
<th data-col="2">π Age</th>
<th data-col="3">π Major</th>
</tr>
</thead>
<tbody>
<tr data-row="0">
<td data-col="0">101</td>
<td data-col="1">Alice</td>
<td data-col="2">20</td>
<td data-col="3">Computer Science</td>
</tr>
<tr data-row="1">
<td data-col="0">102</td>
<td data-col="1">Ben</td>
<td data-col="2">21</td>
<td data-col="3">Mathematics</td>
</tr>
<tr data-row="2">
<td data-col="0">103</td>
<td data-col="1">Chloe</td>
<td data-col="2">19</td>
<td data-col="3">Physics</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="rt-panel" id="rtPanel">
<p class="rt-hint">Use the buttons above, or click the frame, a column header, or a row directly.</p>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.getElementById('reltable');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
(function () {
const frame = document.getElementById('rtFrame');
const table = document.getElementById('rtTable');
const panel = document.getElementById('rtPanel');
const headers = table.querySelectorAll('thead th');
const rows = table.querySelectorAll('tbody tr');
const allCells = table.querySelectorAll('td');
const colInfo = {
0: { name: 'StudentID', desc: 'A unique numeric ID identifying each student β the primary key column.' },
1: { name: 'Name', desc: "A text column storing the student's full name." },
2: { name: 'Age', desc: "A number column storing the student's age." },
3: { name: 'Major', desc: "A text column storing the student's field of study." }
};
function clearAll() {
frame.classList.remove('is-active');
headers.forEach(h => h.classList.remove('is-active'));
rows.forEach(r => r.classList.remove('is-active'));
allCells.forEach(c => c.classList.remove('is-col-active'));
}
function selectRelation() {
clearAll();
frame.classList.add('is-active');
panel.innerHTML =
'<p class="rt-panel-title">Relation = Table</p>' +
'<p class="rt-panel-role">The whole table</p>' +
'<p class="rt-panel-desc">This entire Students table is one Relation. It stores structured data about a single entity β every row and column together.</p>';
}
function selectAttribute(colIndex) {
clearAll();
const th = table.querySelector('thead th[data-col="' + colIndex + '"]');
th.classList.add('is-active');
table.querySelectorAll('td[data-col="' + colIndex + '"]').forEach(td => td.classList.add('is-col-active'));
const meta = colInfo[colIndex];
panel.innerHTML =
'<p class="rt-panel-title">Attribute = Column</p>' +
'<p class="rt-panel-role">' + meta.name + '</p>' +
'<p class="rt-panel-desc">' + meta.desc + '</p>';
}
function selectTuple(rowIndex) {
clearAll();
const tr = table.querySelector('tbody tr[data-row="' + rowIndex + '"]');
tr.classList.add('is-active');
const values = Array.from(tr.querySelectorAll('td')).map(td => td.textContent.trim());
const preview = '(' + values[0] + ', "' + values[1] + '", ' + values[2] + ', "' + values[3] + '")';
panel.innerHTML =
'<p class="rt-panel-title">Tuple = Row</p>' +
'<p class="rt-panel-role">One complete record</p>' +
'<p class="rt-panel-desc">A Tuple holds one value for every attribute. This row reads: ' + preview + '</p>';
}
const btnRelation = document.getElementById('rtBtnRelation');
const btnAttribute = document.getElementById('rtBtnAttribute');
const btnTuple = document.getElementById('rtBtnTuple');
const btns = [btnRelation, btnAttribute, btnTuple];
function setActiveBtn(btn) {
btns.forEach(b => b.classList.remove('is-active'));
if (btn) btn.classList.add('is-active');
}
let attrIndex = -1;
let rowIndex = -1;
btnRelation.addEventListener('click', () => {
setActiveBtn(btnRelation);
selectRelation();
});
btnAttribute.addEventListener('click', () => {
attrIndex = (attrIndex + 1) % headers.length;
setActiveBtn(btnAttribute);
selectAttribute(attrIndex);
});
btnTuple.addEventListener('click', () => {
rowIndex = (rowIndex + 1) % rows.length;
setActiveBtn(btnTuple);
selectTuple(rowIndex);
});
})();
</script>
</body>
</html>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});