レトロなUIとは
みなさんは、レトロなUIというとどういうイメージを持ちますか?
スマホ自体がまだ新しいデバイスなので、レトロというのはピンとこないかも知れません。
iPhoneが登場して、スマホのUIは劇的に進化しました。それからスマホのUIは基本的には全く変わっておらず、レトロ感を出すには、iPhone以前の要素が必要になってきます。
スマホアプリを使っていて、
- 徹底したモードレス
- 必要最小限の機能コンポーネント
- 表示と機能の混在
こういう部分が昔のUIと比べた時に特徴的だなと感じています。
この特徴は、これに慣れ親しんだ人だと全く違和感を感じないと思いますが、ガラケーを長年メインで使ってきた年配者からすると、なかなか思い通りに操作できない要因にもなっています。
ChatGPTに聞いてみた
レトロ感を感じるUIはどのようなものか、ChatGPTと少しやり取りをしてみました。
整理すると、こんな感じになります。
- 色彩と配色
セピア調、モノクロ、パステルカラーなど昔ながらの色使いを採用することで、レトロな雰囲気を演出。 - デザインとパターン
レトロなフォントや手書き風文字、ドット絵やピクセルアート風のデザインを使用。
幾何学模様や花柄など、昔のパターンを取り入れる。 - 質感と素材感の表現
木目調や金属風のテクスチャを背景やアイコンに使用。
ノイズやフィルムグレインを加えてアナログ感を出す。 - 形状とスタイル
丸みを帯びたボタンやアイコン、シンプルで装飾的なデザインを採用。
アナログ機器を模したUI部品や操作感を取り入れる。 - 音とアニメーション
タイプライターの音やフィルムリールの音など、機械的な効果音を使用。
アナログ的なトランジションやレトロなアニメーション効果を加える。
昔のUIの例
インターネットが普及する以前のUIは、CLIが基本でした。それでもエスケープシーケンスを使ってメニュー構造を作り、できるだけ分かりやすいUIを作る工夫をしていました。
また、1984年のMacの登場でGUIという概念が一般に広まり、その直後MicrosoftがWindows1.0をリリースしました。
画面は、ロータス123、Netware、Windows3.1と当時の最先端製品のUIになりますが、いずれも特徴があって、今見ると味があるのではないでしょうか。
レトロなUIの設計
こういった情報を参考に、レトロなUIを考えてみました。
-
昔のWebサイトでよく使われていた手法を使う
色彩は派手に、フォントもプロポーショナルではなく、MSゴシックのような昔からの標準フォントを使う。
古いWebサイトでよく使われいた、frameを再現する。
文字を動かす。 -
タイトルバーなどの表示
古いWindowsで使われていたようなタイトルバーと機能ボタンを再現してみる。
スクロールバーを常時表示させる。 -
地図を表示する場合
Google Mapではなく、OpenStreetMapを使う。色彩やごちゃごちゃしている感じがレトロっぽい。 -
データ入力
古いWindowsのダイアログボックスで使われていたような、グレーのボタンを多用する。
ファイル選択もダイアログ形式とする。
実際に作ったもの
アプリのトップ画面になります。
2000年以前にはやったスタイルです。
ハイライトのための文字が動いているのがレトロ感を出しています。
地図の表示です。
Google Map ではなく、OpenStreetMapを使っています。
データ入力です。
トグルボタンは、Windows3.1のダイアログでよく使うコンポーネントに似せて作っています。
ファイル選択も、formでのファイルアップロード機能をそのまま使っています。もしかしたら、セキュリティ的な問題があるかも知れませんが、要研究です。
以上は、現在制作中のアプリのプロトタイプになります。
ソースコードは以下の通りです。
最初の画面
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="description" content="" />
<meta http-equiv="content-style-type" content="text/css" />
<meta http-equiv="content-script-type" content="text/javascript" />
<meta name="format-detection" content="telephone=no">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>オムクエのホームページ</title>
<link rel="stylesheet" href="topmenu.css" media="all" />
</head>
<body>
<div id="container_wrap">
<header class="header">
<nav id="gnavi">
<ul>
<li><input id="block-01" type="radio" name="menu" class="toggle" onclick="select_menu()" checked>
<label class="Label" for="block-01">トップページ</label></li>
<li><input id="block-02" type="radio" name="menu" class="toggle" onclick="select_menu()">
<label class="Label" for="block-02">オムクエについて</label></li>
<li><input id="block-03" type="radio" name="menu" class="toggle" onclick="select_menu()">
<label class="Label" for="block-03">工事中</label></li>
</ul>
</nav>
</header>
<div class="contents">
<section id="section_1">
<h1 class="main_ttl niji"><span>昔</span><span>な</span><span>が</span><span>ら</span><span>の</span></h1>
<h1 class="main_ttl niji"><span>オ</span><span>ム</span><span>ラ</span><span>イ</span><span>ス</span><span>の</span></h1>
<h1 class="main_ttl niji"><span>世</span><span>界</span><span>へ</span><span>よ</span><span>う</span><span>こ</span><span>そ</span></h1>
<p class="counter m30">あなたは<img alt="アクセスカウンター画像">人目のお客様です</p>
<div class="m30">
<div class="text_attention">
<p>↓続きはこちら↓</p>
</div>
<ul class="list_banner">
<li>
<a href="map.html" class="banner1">オムライス指数を見る</a>
</li>
<li>
<a href="omuregist.html" class="banner2">オムライス情報の<br>登録</a>
</li>
</ul>
</section>
<section id="section_2">
<h2 class="main_ttl niji m50"><span>こ</span><span>の</span><span>ホ</span><span>ー</span><span>ム</span><span>ペ</span><span>ー</span><span>ジ</span><span>に</span><span>つ</span><span>い</span><span>て</span></h2>
<p class="txt_b m30">このホームページはフレーム要素を使っているように見えて、<br>実はフレームは使っていません!</p>
<p class="txt_b">さらに訪問者をカウンターでカウントしているようにみせかけて<br>まったくカウントしていません!!</p>
<div class="text_attention">
<p class="niji"><span>カ</span><span>ウ</span><span>ン</span><span>ト</span><span>し</span><span>て</span><span>い</span><span>ま</span><span>せ</span><span>ん</span></p>
</div>
</section>
<section id="section_3">
<h2 class="main_ttl m50">工事中</h2>
</section>
</div>
</div>
<script>
document.oncontextmenu = function() { alert("右クリック禁止"); return false; }
function select_menu() {
menu = document.getElementsByName('menu')
if (menu[0].checked) {
document.getElementById('section_1').style.display = "";
document.getElementById('section_2').style.display = "none";
document.getElementById('section_3').style.display = "none";
} else if (menu[1].checked) {
document.getElementById('section_1').style.display = "none";
document.getElementById('section_2').style.display = "";
document.getElementById('section_3').style.display = "none";
} else {
document.getElementById('section_1').style.display = "none";
document.getElementById('section_2').style.display = "none";
document.getElementById('section_3').style.display = "";
}
}
window.addEventListener('load', select_menu());
</script>
</body>
</html>
body, div, dl, dt, dd, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, textarea, p, blockquote, th, td, ul {
margin: 0;
padding: 0;
}
body, button, input, select, textarea {
font-size: 100%;
}
img {
vertical-align: top;
font-size: 0;
line-height: 0;
}
* {
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
}
*:before, *:after {
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
}
body {
font: 500 16px/1.8 "游ゴシック", "Yu Gothic", YuGothic, "Hiragino Kaku Gothic ProN", "Hiragino Kaku Gothic Pro", "メイリオ", Meiryo, "MS ゴシック", sans-serif;
color: #21201c;
text-align: left;
letter-spacing: 1px;
}
ul, ol {
list-style: none;
}
a {
text-decoration: none;
color: #383838;
-webkit-transition: 0.3s;
-o-transition: 0.3s;
transition: 0.3s;
}
a:link, a:active, a:visited {
text-decoration: none;
}
#container_wrap{
display: flex;
height: 100vh;
}
#container_wrap .header{
width: 25%;
height: 100%;
position: relative;
background-color: aqua;
border-right: double 5px grey;
}
#container_wrap .contents {
width: 75%;
height: 100%;
background-color: aqua;
text-align: center;
position: relative;
background-image: url('images/star.png');
background-size: 30px 30px;
background-repeat: repeat; /* 繰り返し設定 */
}
.main_ttl{
font-size: 2rem;
font-style: italic;
}
.counter{
display: flex;
justify-content: center;
align-items: center;
font-size: 1.125rem;
font-weight: bold;
}
.m30{
color: rgb(75, 2, 2);
margin-bottom: 1.875rem;
}
.m50{
margin-bottom: 3.125rem;
}
#gnavi{
width: 100%;
position: absolute;
top: 10%;
left: 50%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
}
#gnavi ul{
display: flex;
flex-direction: column;
align-items: center;
}
#gnavi ul li{
margin-bottom: .625rem;
}
#section_1,
#section_2,
#section_3{
position: absolute;
top: 15%;
left: 50%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
}
.toggle{
display: none;
}
.Label{
cursor: pointer;
color: blue;
text-decoration:underline;
}
#gnavi ul li .Label:hover{
color: black;
}
.list_banner{
width: 300px;
display: flex;
justify-content: space-between;
}
.banner1{
width: 120px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
background-color: chartreuse;
font-size: 1.25rem;
font-weight: 800;
font-style: italic;
}
.banner2{
width: 120px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
background-color: blue;
color: #fff;
font-size: .9rem;
font-weight: 800;
font-style: italic;
line-height: 1.3;
}
.txt_b{
font-weight: 800;
}
.bbs{
font-size : 1.875rem;
font-weight: bold;
color: blue;
}
.text_attention{
margin : auto;
width : 100%;
font-size : 1.25rem;
font-weight: 800;
text-align : center;
overflow : hidden;
color: black;
}
.text_attention p{
margin:0;
display : inline-block;
padding-left: 100%;
white-space : nowrap;
line-height : 1em;
animation : scrolltxt 8s linear infinite;
}
@keyframes scrolltxt{
0% { transform: translateX(0)}
100% { transform: translateX(-100%)}
}
.attention{
font-weight: bold;
color: red;
font-size : 1.125rem;
}
.blink {
animation: blinking .2s ease-in-out infinite alternate;
}
@keyframes blinking {
0% {opacity: 0;}
100% {opacity: 1;}
}
.niji span:nth-of-type(1),
.niji span:nth-of-type(12){
color: blueviolet;
}
.niji span:nth-of-type(2),
.niji span:nth-of-type(13){
color: darkviolet;
}
.niji span:nth-of-type(3),
.niji span:nth-of-type(14){
color: blue;
}
.niji span:nth-of-type(4),
.niji span:nth-of-type(15){
color: green;
}
.niji span:nth-of-type(5),
.niji span:nth-of-type(16){
color: rgb(255, 109, 47);
}
.niji span:nth-of-type(6),
.niji span:nth-of-type(17){
color: yellowgreen;
}
.niji span:nth-of-type(7),
.niji span:nth-of-type(18){
color: yellow;
}
.niji span:nth-of-type(8){
color: orange;
}
.niji span:nth-of-type(9){
color: orangered;
}
.niji span:nth-of-type(10){
color: red;
}
.niji span:nth-of-type(11){
color: fuchsia;
}
地図の表示
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/simplebar@5.3.6/dist/simplebar.min.css"/>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/simplebar@5.3.6/dist/simplebar.min.js"></script>
<title>オムライス指数マップ</title>
<style>
.window {
border: 2px solid black;
background-color: #FFFFFF;
/* box-shadow: 3px 3px #808080; */
position: relative;
overflow: hidden;
}
.title-bar {
background-color: #000080;
color: white;
padding: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.title {
font-size: 14px;
}
/* マップのサイズを指定 */
#map {
height: 50vh;
width: 100%;
}
/* オムライス指数リストのスタイル */
#omurice-list {
overflow-y: scroll;
border: 1px solid #ccc; /* 見やすさのためにボーダーを追加(任意) */
padding: 10px;
background-color: #f0f0f0;
}
.content {
padding: 10px;
height: 260px; /* タイトルバーの分を引いた高さ */
overflow-y: scroll; /* 常にスクロールバーを表示 */
font-size: 12px;
background-color: white;
box-sizing: border-box;
-webkit-overflow-scrolling: touch; /* iOSでのスムーズスクロール */
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #333;
color: white;
}
</style>
<!-- Leaflet.jsのCSSを読み込み -->
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
</head>
<body>
<div class="window">
<div class="title-bar">
<span class="title">OmuQue Manager</span>
<div class="buttons">
<button class="minimize">-</button>
<button class="close">x</button>
</div>
</div>
<div id="map"></div>
<div class="content" data-simplebar data-simplebar-auto-hide="false">
<div id="omurice-list">
<table>
<thead>
<tr>
<th>駅名</th>
<th>オムライス指数</th>
</tr>
</thead>
<tbody id="station-list">
<!-- 駅とオムライス指数がここに動的に表示されます -->
</tbody>
</table>
</div>
</div>
</div>
<!-- Leaflet.jsのJavaScriptを読み込み -->
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script>
let map;
// Leafletマップの初期化
function initMap() {
// 東京駅を初期中心に設定
map = L.map('map').setView([35.681236, 139.767125], 12);
// OpenStreetMapのタイルレイヤーを追加
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
// 駅データを取得してマップに表示
fetchStationData();
}
// 駅データの取得(ここでは一例として静的なデータを使用)
function fetchStationData() {
const stations = [
{ name: '麻布十番駅', lat: 35.658033, lng: 139.701636, omuriceIndex: 31 },
{ name: '四谷駅', lat: 35.685372, lng: 139.7287997, omuriceIndex: 31 },
{ name: '中目黒駅', lat: 35.6441631, lng: 139.6988444, omuriceIndex: 29 },
{ name: '東京駅', lat: 35.681236, lng: 139.767125, omuriceIndex: 27 },
{ name: '新宿駅', lat: 35.690921, lng: 139.700257, omuriceIndex: 27 },
{ name: '渋谷駅', lat: 35.658033, lng: 139.701636, omuriceIndex: 27 },
// 他の駅データを追加
];
// 駅の位置にマーカーを表示し、リストを更新
updateMapAndList(stations);
}
// 駅のデータを基にマップとリストを更新
function updateMapAndList(stations) {
const stationList = document.getElementById('station-list');
stationList.innerHTML = ''; // 既存のリストをクリア
stations.forEach(station => {
// マップにマーカーを追加
const marker = L.marker([station.lat, station.lng]).addTo(map);
marker.bindPopup(`<b>${station.name}</b><br>オムライス指数: ${station.omuriceIndex}`);
// リストにオムライス指数を追加
const row = document.createElement('tr');
row.innerHTML = `<td>${station.name}</td><td>${station.omuriceIndex}</td>`;
stationList.appendChild(row);
});
}
// ページ読み込み時にマップを初期化
window.onload = initMap;
</script>
</body>
</html>
データ入力画面
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/simplebar@5.3.6/dist/simplebar.min.css"/>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/simplebar@5.3.6/dist/simplebar.min.js"></script>
<title>オムライス登録</title>
<style>
.content {
padding: 10px;
overflow-y: scroll; /* 常にスクロールバーを表示 */
font-size: 14px;
background-color: rgb(221, 221, 221);
box-sizing: border-box;
-webkit-overflow-scrolling: touch; /* iOSでのスムーズスクロール */
}
main {
padding: 10px;
overflow-y: scroll; /* 常にスクロールバーを表示 */
font-size: 14px;
background-color: rgb(221, 221, 221);
box-sizing: border-box;
-webkit-overflow-scrolling: touch; /* iOSでのスムーズスクロール */
}
.text label, input {
display: inline-block;
margin-right: 10px; /* ラベルと入力欄の間に余白を追加 */
}
.file-upload label, input {
display: inline-block;
margin-right: 10px; /* ラベルと入力欄の間に余白を追加 */
}
.text input {
width: 200px; /* 入力欄の幅を調整 */
}
.input-window form {
background-color: rgb(221, 221, 221);
}
.complete-message {
display: none;
padding: 10px;
margin-top: 10px;
border: 2px solid #808080;
background-color: #d4d0c8;
box-shadow: inset 1px 1px 0 #ffffff, inset -1px -1px 0 #000000;
font-size: 12px;
text-align: center;
}
</style>
</head>
<body>
<div class="window">
<div class="title-bar">
<span class="title">OmuQue Manager</span>
<div class="buttons">
<button class="minimize">-</button>
<button class="close">x</button>
</div>
</div>
<div class="content" data-simplebar data-simplebar-auto-hide="false">
<header>オムライス登録</header>
<main>
<form id="uploadForm" method="post" enctype="multipart/form-data">
<!-- 駅のテキスト入力 -->
<div class="text">
<label for="station">駅名:</label>
<input type="text" name="station" id="station" required>
</div>
<!-- 卵の種類 -->
<div class="choices">
<p>卵の種類:</p>
<label>
<input type="radio" name="egg" value="5"> しっかり焼いた薄焼き卵 <br>
</label>
<label>
<input type="radio" name="egg" value="3"> バターたっぷりふわトロ <br>
</label>
<label>
<input type="radio" name="egg" value="1"> 野菜などが入っている <br>
</label>
<label>
<input type="radio" name="egg" value="0"> その他 <br>
</label>
</div>
<!-- ライスの種類 -->
<div class="choices">
<p>ライスの種類:</p>
<label>
<input type="radio" name="rice" value="10"> チキンライス <br>
</label>
<label>
<input type="radio" name="rice" value="5"> ケチャップライス <br>
</label>
<label>
<input type="radio" name="rice" value="2"> ピラフ <br>
</label>
<label>
<input type="radio" name="rice" value="0"> 白飯 <br>
</label>
</div>
<!-- ソースの種類 -->
<div class="choices">
<p>ソースの種類:</p>
<label>
<input type="radio" name="sauce" value="5"> ケチャップ <br>
</label>
<label>
<input type="radio" name="sauce" value="4"> ケチャップベースのソース <br>
</label>
<label>
<input type="radio" name="sauce" value="2"> デミグラスソース <br>
</label>
<label>
<input type="radio" name="sauce" value="0"> その他 <br>
</label>
</div>
<!-- 画像アップロードフィールド -->
<div class="file-upload">
<label for="file">画像をアップロード:</label>
<input type="file" id="file" name="file" accept="image/*" required>
</div>
<!-- 送信ボタン -->
<div class="button-container">
<input type="submit" value="送信" class="button">
</div>
</form>
<dev id="completeMessage" class="complete-message">完了しました</div>
</main>
<footer>(c) OmuQuest</footer>
</div>
</div>
<script>
document.getElementById('uploadForm').addEventListener('submit', function (e) {
e.preventDefault();
// ファイルアップロード処理をシミュレート(ここに実際の処理を追加)
setTimeout(function () {
// アップロード完了後に完了メッセージを表示
document.getElementById('completeMessage').style.display = 'block';
}, 1000); // 1秒後に完了表示
});
/*
document.querySelector('form').addEventListener('submit', async (event) => {
const formData = new FormData(event.target);
// 画像ファイルを取得
const fileInput = document.querySelector('#file');
const file = fileInput.files[0];
if (file) {
const reader = new FileReader();
reader.onloadend = async () => {
const imageData = reader.result; // Base64に変換された画像
// フォームデータと画像データをサーバーに送信
const response = await fetch('/.netlify/functions/upload', {
method: 'POST',
body: JSON.stringify({
station: formData.get('station'), // 駅名をテキストで取得
egg: formData.get('egg'),
rice: formData.get('rice'),
sauce: formData.get('sauce'),
fileName: file.name, // ファイル名を取得
image: imageData // Base64に変換された画像データ
}),
headers: {
'Content-Type': 'application/json',
},
});
const result = await response.json();
console.log(result.message);
};
reader.readAsDataURL(file); // 画像をBase64に変換
}
});
*/
</script>
</body>
</html>
body {
background-color: #C0C0C0;
font-family: 'Courier New', Courier, monospace;
margin: 0;
padding: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.window {
width: 380px;
height: 630px;
border: 2px solid black;
background-color: #FFFFFF;
box-shadow: 3px 3px #808080;
position: relative;
overflow: hidden;
}
.title-bar {
background-color: #000080;
color: white;
padding: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.title {
font-size: 14px;
}
.buttons {
display: flex;
}
.buttons button {
background-color: white;
border: 1px solid black;
width: 20px;
height: 20px;
margin-left: 5px;
font-weight: bold;
}
.content {
padding: 10px;
height: calc(100% - 40px); /* タイトルバーの分を引いた高さ */
overflow-y: scroll; /* 常にスクロールバーを表示 */
font-size: 14px;
background-color: white
box-sizing: border-box;
-webkit-overflow-scrolling: touch; /* iOSでのスムーズスクロール */
}
/* スクロールバーのスタイル */
.simplebar-track.simplebar-horizontal {
height: 10px;
background: #B5B5B5;
border-radius: 5px;
max-width: 372px;
margin: 0 auto;
}
.simplebar-scrollbar {
height: 10px;
background: #41485B;
border-radius: 5px;
top: 0;
}
header, footer {
background-color: #000080;
color: white;
padding: 10px;
text-align: center;
}
main {
padding: 20px;
background-color: white;
border: 2px solid black;
/* width: 400px; */
margin: auto;
}
.text, .choices {
margin-bottom: 20px;
}
.button-container {
text-align: center;
}
.button {
background-color: #C0C0C0;
border: 1px solid black;
padding: 10px 20px;
cursor: pointer;
}
body {
background-color: #c0c0c0; /* 古いWindowsのような背景色 */
}
.choices {
border: 1px solid gray;
padding: 10px;
display: inline-block;
background-color: #d4d0c8; /* 古いWindowsのような背景色 */
}
legend {
font-size: 12px;
padding: 0 5px;
background-color: #d4d0c8;
/* border: 2px groove; */
}
input[type="radio"] {
margin: 5px;
}
label {
font-family: "MS UI Gothic", sans-serif;
font-size: 12px;
display: flex;
align-items: center;
background-color: #d4d0c8;
border: 2px solid #808080;
padding: 5px;
margin-bottom: 5px;
box-shadow: inset 1px 1px white, inset -1px -1px gray;
}
label:hover {
background-color: #e4e0dc;
}
input[type="text"] {
width: 200px;
height: 25px;
padding: 2px;
background-color: #ffffff; /* グレーの背景色 */
border: 2px solid #808080; /* グレーのボーダーでWindows風に */
box-shadow: inset 1px 1px 0 #ffffff, inset -1px -1px 0 #000000; /* 立体感を追加 */
font-family: "MS UI Gothic", sans-serif; /* Windows 95/98風のフォント */
font-size: 12px;
color: black;
}
input[type="text"]:focus {
outline: none; /* フォーカス時のデフォルトの枠を削除 */
border: 2px solid #000080; /* フォーカス時のボーダーを濃い青に */
}
input[type="text"]:hover {
border-color: #808080; /* ホバー時のボーダー */
}
以上、まだ少し冗長な所もありますが、UIのプロトタイプということで作成してみました。
スクロールバーの表示については、このサイトを参考にしました。
最後に
レトロ感を出すためには、見た目もありますし、実際に使用した際にもちょっと使いにくさを演出する必要があると考えています。
これは、古い車がマニュアル車だったり、パワステやカーナビ、バックカメラなども付いていなかったりすることに似ています。
だからといって、ユーザビリティをあまりにも落としてしまったら、ユーザーが上手く使えなくなってしまうので、バランスを考えて実装することが必要です。
そのあたりを考えながら、今後アプリの実装を進めていきたいと思います。
スマホのUIについては研究途上なので、私の理解が不足している部分もあるかも知れません。詳しい方がいらっしゃればコメント頂ければ嬉しいです。