commit e6d335f5b5fa579147b429dc799eb5a7bf928b06 Author: Ryan Malloy Date: Sun Aug 17 23:35:55 2025 -0600 Initial commit: story-teller.ink platform - Complete Astro + Alpine.js implementation - Docker Compose setup with Caddy reverse proxy - Dual platform: Anonymous & Named Storytellers - Interactive features: voting, comments, filtering - Categories page with search functionality - Content collections for markdown stories - Responsive design with accessibility features - Environment variable configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude diff --git a/.astro/collections/dignity.schema.json b/.astro/collections/dignity.schema.json new file mode 100644 index 0000000..2568841 --- /dev/null +++ b/.astro/collections/dignity.schema.json @@ -0,0 +1,76 @@ +{ + "$ref": "#/definitions/dignity", + "definitions": { + "dignity": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "excerpt": { + "type": "string" + }, + "authorName": { + "type": "string" + }, + "authorAge": { + "type": "number" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "location": { + "type": "string" + }, + "dateOfEvent": { + "type": "string" + }, + "upvotes": { + "type": "number", + "default": 0 + }, + "isPromoted": { + "type": "boolean", + "default": false + }, + "originalStoryId": { + "type": "string" + }, + "publishedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "string", + "format": "date" + }, + { + "type": "integer", + "format": "unix-time" + } + ] + }, + "commentCount": { + "type": "number", + "default": 0 + }, + "$schema": { + "type": "string" + } + }, + "required": [ + "title", + "authorName", + "publishedAt" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/.astro/collections/nevertell.schema.json b/.astro/collections/nevertell.schema.json new file mode 100644 index 0000000..d49f08c --- /dev/null +++ b/.astro/collections/nevertell.schema.json @@ -0,0 +1,70 @@ +{ + "$ref": "#/definitions/nevertell", + "definitions": { + "nevertell": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "excerpt": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "location": { + "type": "string" + }, + "dateOfEvent": { + "type": "string" + }, + "upvotes": { + "type": "number", + "default": 0 + }, + "promotionVotes": { + "type": "number", + "default": 0 + }, + "isPromoted": { + "type": "boolean", + "default": false + }, + "publishedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "string", + "format": "date" + }, + { + "type": "integer", + "format": "unix-time" + } + ] + }, + "commentCount": { + "type": "number", + "default": 0 + }, + "$schema": { + "type": "string" + } + }, + "required": [ + "title", + "publishedAt" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/.astro/content-assets.mjs b/.astro/content-assets.mjs new file mode 100644 index 0000000..2b8b823 --- /dev/null +++ b/.astro/content-assets.mjs @@ -0,0 +1 @@ +export default new Map(); \ No newline at end of file diff --git a/.astro/content-modules.mjs b/.astro/content-modules.mjs new file mode 100644 index 0000000..2b8b823 --- /dev/null +++ b/.astro/content-modules.mjs @@ -0,0 +1 @@ +export default new Map(); \ No newline at end of file diff --git a/.astro/content.d.ts b/.astro/content.d.ts new file mode 100644 index 0000000..a2ef744 --- /dev/null +++ b/.astro/content.d.ts @@ -0,0 +1,219 @@ +declare module 'astro:content' { + export interface RenderResult { + Content: import('astro/runtime/server/index.js').AstroComponentFactory; + headings: import('astro').MarkdownHeading[]; + remarkPluginFrontmatter: Record; + } + interface Render { + '.md': Promise; + } + + export interface RenderedContent { + html: string; + metadata?: { + imagePaths: Array; + [key: string]: unknown; + }; + } +} + +declare module 'astro:content' { + type Flatten = T extends { [K: string]: infer U } ? U : never; + + export type CollectionKey = keyof AnyEntryMap; + export type CollectionEntry = Flatten; + + export type ContentCollectionKey = keyof ContentEntryMap; + export type DataCollectionKey = keyof DataEntryMap; + + type AllValuesOf = T extends any ? T[keyof T] : never; + type ValidContentEntrySlug = AllValuesOf< + ContentEntryMap[C] + >['slug']; + + export type ReferenceDataEntry< + C extends CollectionKey, + E extends keyof DataEntryMap[C] = string, + > = { + collection: C; + id: E; + }; + export type ReferenceContentEntry< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}) = string, + > = { + collection: C; + slug: E; + }; + export type ReferenceLiveEntry = { + collection: C; + id: string; + }; + + /** @deprecated Use `getEntry` instead. */ + export function getEntryBySlug< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}), + >( + collection: C, + // Note that this has to accept a regular string too, for SSR + entrySlug: E, + ): E extends ValidContentEntrySlug + ? Promise> + : Promise | undefined>; + + /** @deprecated Use `getEntry` instead. */ + export function getDataEntryById( + collection: C, + entryId: E, + ): Promise>; + + export function getCollection>( + collection: C, + filter?: (entry: CollectionEntry) => entry is E, + ): Promise; + export function getCollection( + collection: C, + filter?: (entry: CollectionEntry) => unknown, + ): Promise[]>; + + export function getLiveCollection( + collection: C, + filter?: LiveLoaderCollectionFilterType, + ): Promise< + import('astro').LiveDataCollectionResult, LiveLoaderErrorType> + >; + + export function getEntry< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}), + >( + entry: ReferenceContentEntry, + ): E extends ValidContentEntrySlug + ? Promise> + : Promise | undefined>; + export function getEntry< + C extends keyof DataEntryMap, + E extends keyof DataEntryMap[C] | (string & {}), + >( + entry: ReferenceDataEntry, + ): E extends keyof DataEntryMap[C] + ? Promise + : Promise | undefined>; + export function getEntry< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}), + >( + collection: C, + slug: E, + ): E extends ValidContentEntrySlug + ? Promise> + : Promise | undefined>; + export function getEntry< + C extends keyof DataEntryMap, + E extends keyof DataEntryMap[C] | (string & {}), + >( + collection: C, + id: E, + ): E extends keyof DataEntryMap[C] + ? string extends keyof DataEntryMap[C] + ? Promise | undefined + : Promise + : Promise | undefined>; + export function getLiveEntry( + collection: C, + filter: string | LiveLoaderEntryFilterType, + ): Promise, LiveLoaderErrorType>>; + + /** Resolve an array of entry references from the same collection */ + export function getEntries( + entries: ReferenceContentEntry>[], + ): Promise[]>; + export function getEntries( + entries: ReferenceDataEntry[], + ): Promise[]>; + + export function render( + entry: AnyEntryMap[C][string], + ): Promise; + + export function reference( + collection: C, + ): import('astro/zod').ZodEffects< + import('astro/zod').ZodString, + C extends keyof ContentEntryMap + ? ReferenceContentEntry> + : ReferenceDataEntry + >; + // Allow generic `string` to avoid excessive type errors in the config + // if `dev` is not running to update as you edit. + // Invalid collection names will be caught at build time. + export function reference( + collection: C, + ): import('astro/zod').ZodEffects; + + type ReturnTypeOrOriginal = T extends (...args: any[]) => infer R ? R : T; + type InferEntrySchema = import('astro/zod').infer< + ReturnTypeOrOriginal['schema']> + >; + + type ContentEntryMap = { + + }; + + type DataEntryMap = { + "dignity": Record; + rendered?: RenderedContent; + filePath?: string; +}>; +"nevertell": Record; + rendered?: RenderedContent; + filePath?: string; +}>; + + }; + + type AnyEntryMap = ContentEntryMap & DataEntryMap; + + type ExtractLoaderTypes = T extends import('astro/loaders').LiveLoader< + infer TData, + infer TEntryFilter, + infer TCollectionFilter, + infer TError + > + ? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError } + : { data: never; entryFilter: never; collectionFilter: never; error: never }; + type ExtractDataType = ExtractLoaderTypes['data']; + type ExtractEntryFilterType = ExtractLoaderTypes['entryFilter']; + type ExtractCollectionFilterType = ExtractLoaderTypes['collectionFilter']; + type ExtractErrorType = ExtractLoaderTypes['error']; + + type LiveLoaderDataType = + LiveContentConfig['collections'][C]['schema'] extends undefined + ? ExtractDataType + : import('astro/zod').infer< + Exclude + >; + type LiveLoaderEntryFilterType = + ExtractEntryFilterType; + type LiveLoaderCollectionFilterType = + ExtractCollectionFilterType; + type LiveLoaderErrorType = ExtractErrorType< + LiveContentConfig['collections'][C]['loader'] + >; + + export type ContentConfig = typeof import("../src/content/config.js"); + export type LiveContentConfig = never; +} diff --git a/.astro/data-store.json b/.astro/data-store.json new file mode 100644 index 0000000..0ed6e52 --- /dev/null +++ b/.astro/data-store.json @@ -0,0 +1 @@ +[["Map",1,2,9,10,70,71],"meta::meta",["Map",3,4,5,6,7,8],"astro-version","5.13.2","content-config-digest","67a4af7ae20c6445","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"site\":\"https://st.l.supported.systems\",\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":\"0.0.0.0\",\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false},\"legacy\":{\"collections\":false}}","nevertell",["Map",11,12,41,42],"college-roommate-prank",{"id":11,"data":13,"body":27,"filePath":28,"digest":29,"rendered":30,"legacyId":40},{"title":14,"excerpt":15,"tags":16,"location":20,"dateOfEvent":21,"upvotes":22,"promotionVotes":23,"isPromoted":24,"publishedAt":25,"commentCount":26},"College Roommate Shenanigans That Almost Got Us Expelled","It involved a dean's car, three chickens, and a very confused security guard...",[17,18,19],"college","1960s","pranks","Golden Years Community, Boston MA","Spring 1963",89,12,false,["Date","2024-01-12T00:00:00.000Z"],32,"Spring of 1963, my roommate Jimmy and I were facing our final exams, and we were stressed out of our minds. We'd been cooped up in the library for weeks, and I think we both went a little stir-crazy.\n\nIt started as a joke when we saw Dean Morrison's brand new Cadillac parked in his reserved spot. Jimmy said, \"Wouldn't it be funny if...\" and I should have stopped him right there, but I didn't.\n\nThe plan was ridiculous: we were going to \"redecorate\" his car. Nothing permanent, just something that would make him scratch his head. We bought three live chickens from a farmer outside town (don't ask me how we got them back to campus), and a bunch of balloons.\n\nAt 2 AM, we snuck out to the parking lot. The security guard was making his rounds, so we had to time it perfectly. We tied the balloons to the car's antenna and bumpers, then - and this is where it gets really stupid - we put the chickens inside the car.\n\nHow did we get into a locked car? Jimmy's dad was a locksmith, and he'd taught Jimmy a thing or two. We figured the chickens would just sit there looking confused, we'd get a laugh, and then we'd let them out before morning.\n\nWhat we didn't account for was that chickens don't just sit quietly. By morning, those birds had completely destroyed the interior of Dean Morrison's brand new Cadillac. Feathers everywhere, you-know-what on every surface, and somehow one of them had figured out how to honk the horn repeatedly.\n\nThe security guard found the car at 6 AM with balloons bobbing in the breeze and chickens raising absolute hell inside. Half the campus came out to see what the commotion was about.\n\nDean Morrison was... not amused. Jimmy and I were called into his office that afternoon, where he sat behind his desk with feathers still stuck in his hair from trying to retrieve his car keys from the backseat.\n\n\"Gentlemen,\" he said, \"I have two questions. First: why chickens? Second: how did you plan to explain this to my wife?\"\n\nWe told him the truth - that we were stressed about exams and thought it would be funny. He stared at us for what felt like an hour, then started laughing so hard he couldn't breathe.\n\nTurns out, he and his fraternity brothers had done something similar to the previous dean twenty years earlier, except with a cow (don't ask me how they got a cow up to the third floor of the administration building).\n\nOur punishment? We had to wash and detail his car every week for the rest of the semester, and we had to take the chickens back to the farmer and explain to him why we were returning slightly traumatized poultry.\n\nJimmy and I graduated that spring, and Dean Morrison gave us each a small rubber chicken with our diplomas. I kept mine for sixty years until my granddaughter finally convinced me to tell her this story. Now it sits on her desk at college, and I suspect she's planning her own shenanigans.\n\nSome traditions never die, they just get passed down to the next generation of troublemakers.","src/content/nevertell/college-roommate-prank.md","db557885d66cb47c",{"html":31,"metadata":32},"\u003Cp>Spring of 1963, my roommate Jimmy and I were facing our final exams, and we were stressed out of our minds. We’d been cooped up in the library for weeks, and I think we both went a little stir-crazy.\u003C/p>\n\u003Cp>It started as a joke when we saw Dean Morrison’s brand new Cadillac parked in his reserved spot. Jimmy said, “Wouldn’t it be funny if…” and I should have stopped him right there, but I didn’t.\u003C/p>\n\u003Cp>The plan was ridiculous: we were going to “redecorate” his car. Nothing permanent, just something that would make him scratch his head. We bought three live chickens from a farmer outside town (don’t ask me how we got them back to campus), and a bunch of balloons.\u003C/p>\n\u003Cp>At 2 AM, we snuck out to the parking lot. The security guard was making his rounds, so we had to time it perfectly. We tied the balloons to the car’s antenna and bumpers, then - and this is where it gets really stupid - we put the chickens inside the car.\u003C/p>\n\u003Cp>How did we get into a locked car? Jimmy’s dad was a locksmith, and he’d taught Jimmy a thing or two. We figured the chickens would just sit there looking confused, we’d get a laugh, and then we’d let them out before morning.\u003C/p>\n\u003Cp>What we didn’t account for was that chickens don’t just sit quietly. By morning, those birds had completely destroyed the interior of Dean Morrison’s brand new Cadillac. Feathers everywhere, you-know-what on every surface, and somehow one of them had figured out how to honk the horn repeatedly.\u003C/p>\n\u003Cp>The security guard found the car at 6 AM with balloons bobbing in the breeze and chickens raising absolute hell inside. Half the campus came out to see what the commotion was about.\u003C/p>\n\u003Cp>Dean Morrison was… not amused. Jimmy and I were called into his office that afternoon, where he sat behind his desk with feathers still stuck in his hair from trying to retrieve his car keys from the backseat.\u003C/p>\n\u003Cp>“Gentlemen,” he said, “I have two questions. First: why chickens? Second: how did you plan to explain this to my wife?”\u003C/p>\n\u003Cp>We told him the truth - that we were stressed about exams and thought it would be funny. He stared at us for what felt like an hour, then started laughing so hard he couldn’t breathe.\u003C/p>\n\u003Cp>Turns out, he and his fraternity brothers had done something similar to the previous dean twenty years earlier, except with a cow (don’t ask me how they got a cow up to the third floor of the administration building).\u003C/p>\n\u003Cp>Our punishment? We had to wash and detail his car every week for the rest of the semester, and we had to take the chickens back to the farmer and explain to him why we were returning slightly traumatized poultry.\u003C/p>\n\u003Cp>Jimmy and I graduated that spring, and Dean Morrison gave us each a small rubber chicken with our diplomas. I kept mine for sixty years until my granddaughter finally convinced me to tell her this story. Now it sits on her desk at college, and I suspect she’s planning her own shenanigans.\u003C/p>\n\u003Cp>Some traditions never die, they just get passed down to the next generation of troublemakers.\u003C/p>",{"headings":33,"localImagePaths":34,"remoteImagePaths":35,"frontmatter":36,"imagePaths":39},[],[],[],{"title":14,"excerpt":15,"tags":37,"location":20,"dateOfEvent":21,"upvotes":22,"promotionVotes":23,"publishedAt":38,"commentCount":26},[17,18,19],["Date","2024-01-12T00:00:00.000Z"],[],"college-roommate-prank.md","elvis-concert-1956",{"id":41,"data":43,"body":56,"filePath":57,"digest":58,"rendered":59,"legacyId":69},{"title":44,"excerpt":45,"tags":46,"location":50,"dateOfEvent":51,"upvotes":52,"promotionVotes":53,"isPromoted":24,"publishedAt":54,"commentCount":55},"The Time I Snuck Out to See Elvis","My parents thought I was at Bible study, but I was actually...",[47,48,49],"1950s","music","rebellion","Sunset Manor, Memphis TN","Summer 1956",127,23,["Date","2024-01-15T00:00:00.000Z"],45,"My parents thought I was at Bible study, but I was actually waiting outside the back door of the community center where Elvis was performing his first paid concert in our little town.\n\nI was seventeen and had never disobeyed my parents before, but something about that music just called to me. When I heard he was coming to town, I knew I had to see him, even if it meant lying to my family.\n\nThe plan was simple: tell my parents I was going to Wednesday night Bible study (which I never missed), but instead sneak around to the back of the community center where the colored folks had to enter. I figured if I stood there, I might be able to hear the music.\n\nWhat I didn't expect was for Elvis himself to come out that back door during his break! He was just a young man then, probably only a few years older than me, and when he saw me standing there in my church dress, he smiled and said, \"Well hello there, darlin'. You waitin' for someone?\"\n\nI was so nervous I could barely speak, but I managed to tell him I just wanted to hear the music. He laughed - not mean, but kind - and said, \"Well shoot, come on in then. Music's for everyone.\"\n\nAnd that's how I ended up dancing to Elvis Presley before anyone knew who he was, in a room full of people my parents would have been scandalized to know I was mixing with. But you know what? It was the most alive I'd ever felt.\n\nWhen I got home three hours later, I told my parents Bible study ran long because we were discussing \"joyful noise unto the Lord.\" It wasn't even a lie, really.\n\nI never told them the truth, even after Elvis became famous. But every time I heard \"That's All Right\" on the radio, I'd smile and remember the night I learned that sometimes the most important lessons come from the most unexpected places.\n\n*[This story received 23 votes for promotion to dignity.ink, but the author chose to keep it anonymous]*","src/content/nevertell/elvis-concert-1956.md","feed48b2ffc95eb7",{"html":60,"metadata":61},"\u003Cp>My parents thought I was at Bible study, but I was actually waiting outside the back door of the community center where Elvis was performing his first paid concert in our little town.\u003C/p>\n\u003Cp>I was seventeen and had never disobeyed my parents before, but something about that music just called to me. When I heard he was coming to town, I knew I had to see him, even if it meant lying to my family.\u003C/p>\n\u003Cp>The plan was simple: tell my parents I was going to Wednesday night Bible study (which I never missed), but instead sneak around to the back of the community center where the colored folks had to enter. I figured if I stood there, I might be able to hear the music.\u003C/p>\n\u003Cp>What I didn’t expect was for Elvis himself to come out that back door during his break! He was just a young man then, probably only a few years older than me, and when he saw me standing there in my church dress, he smiled and said, “Well hello there, darlin’. You waitin’ for someone?”\u003C/p>\n\u003Cp>I was so nervous I could barely speak, but I managed to tell him I just wanted to hear the music. He laughed - not mean, but kind - and said, “Well shoot, come on in then. Music’s for everyone.”\u003C/p>\n\u003Cp>And that’s how I ended up dancing to Elvis Presley before anyone knew who he was, in a room full of people my parents would have been scandalized to know I was mixing with. But you know what? It was the most alive I’d ever felt.\u003C/p>\n\u003Cp>When I got home three hours later, I told my parents Bible study ran long because we were discussing “joyful noise unto the Lord.” It wasn’t even a lie, really.\u003C/p>\n\u003Cp>I never told them the truth, even after Elvis became famous. But every time I heard “That’s All Right” on the radio, I’d smile and remember the night I learned that sometimes the most important lessons come from the most unexpected places.\u003C/p>\n\u003Cp>\u003Cem>[This story received 23 votes for promotion to dignity.ink, but the author chose to keep it anonymous]\u003C/em>\u003C/p>",{"headings":62,"localImagePaths":63,"remoteImagePaths":64,"frontmatter":65,"imagePaths":68},[],[],[],{"title":44,"excerpt":45,"tags":66,"location":50,"dateOfEvent":51,"upvotes":52,"promotionVotes":53,"publishedAt":67,"commentCount":55},[47,48,49],["Date","2024-01-15T00:00:00.000Z"],[],"elvis-concert-1956.md","dignity",["Map",72,73,104,105],"dorothy-civil-rights",{"id":72,"data":74,"body":90,"filePath":91,"digest":92,"rendered":93,"legacyId":103},{"title":75,"excerpt":76,"authorName":77,"authorAge":78,"tags":79,"location":83,"dateOfEvent":84,"upvotes":85,"isPromoted":86,"originalStoryId":87,"publishedAt":88,"commentCount":89},"From Anonymous to Proud: My Journey Fighting for Civil Rights","Originally shared anonymously on nevertell.ink, Dorothy Mae Johnson decided to tell her full story...","Dorothy Mae Johnson",83,[80,18,81,82],"civil rights","activism","courage","Golden Years Community, Atlanta GA","1963-1968",312,true,"dorothy-bus-boycott",["Date","2024-01-08T00:00:00.000Z"],143,"*Editor's note: This story was originally shared anonymously on nevertell.ink, where it received over 150 votes for promotion. After much encouragement from the community, Dorothy Mae Johnson decided to share her full story and put her name to this important piece of history.*\n\n---\n\nFor sixty years, I kept quiet about my role in the civil rights movement. Not because I was ashamed, but because I was taught that good work doesn't need recognition. My grandmother used to say, \"Do what's right because it's right, not because folks are watching.\" But at 83, I've decided some stories need to be told with names attached, especially when young people today are still fighting for justice.\n\nIn 1963, I was a 22-year-old teacher at a segregated elementary school in Montgomery, Alabama. I was making $32 a week teaching 47 children in a classroom meant for 25, using textbooks that were ten years out of date and had already been discarded by the white schools across town.\n\nWhen Dr. King and the others organized the Montgomery Bus Boycott, I knew I had to be part of it, even though it meant walking four miles to work every morning and four miles home every evening. My principal warned all of us teachers that we could lose our jobs if we were caught participating in \"agitation activities.\" But how could I teach my students about dignity and self-respect if I wasn't willing to fight for my own?\n\nThe hardest part wasn't the walking - though Lord knows my feet were screaming by December. The hardest part was the fear. Every morning when I left my apartment, I didn't know if I'd make it to school safely. There were cars full of angry white men who would drive slowly beside us as we walked, shouting things I won't repeat, sometimes throwing things.\n\nBut we weren't alone. The solidarity among the Black community during those 381 days was something I'll never forget. People who owned cars organized carpools. Folks who lived along the walking routes would set up water stations and first aid stops. My neighbor, Mrs. Washington, would make an extra sandwich every morning and press it into my hand as I walked past her house.\n\nOne morning in February, it was particularly cold and rainy. I was walking with a group of other teachers and domestic workers when a police car pulled up beside us. My heart started racing - we all knew stories of people being arrested for \"loitering\" or \"disturbing the peace\" for simply walking down the street.\n\nBut instead of arresting us, the officer rolled down his window and said, \"You folks need a ride?\" We looked at each other, confused. Then he said, quietly, \"My daughter goes to your school, Miss Johnson. She talks about you all the time. Says you're the best teacher she's ever had.\"\n\nIt turned out Officer Williams was one of the few Black police officers in Montgomery, and he'd been secretly helping with the boycott by giving rides when he could do so safely. That day, he drove all six of us teachers to school, and we arrived dry and warm for the first time in months.\n\nAfter the boycott succeeded, I thought the hardest part was over. I was wrong again. The real work was just beginning. I spent the next five years helping to integrate schools, registering voters, and organizing community meetings. I was arrested three times - once for \"trespassing\" when I tried to register to vote at the white courthouse, once for \"disturbing the peace\" during a peaceful protest, and once for \"conspiracy\" when we organized a voter registration drive.\n\nEach arrest was terrifying, but they were also clarifying. Every time they put me in that cell, I became more certain that what we were doing was not just right, but necessary. My students needed to grow up in a world where their worth wasn't determined by the color of their skin.\n\nThe most meaningful moment came in 1968, when the first integrated class graduated from Montgomery High School. I was teaching at the newly integrated elementary school by then, and several of my former students were in that graduating class. As I watched them walk across that stage - Black and white students together - I thought about all the miles we'd walked, all the risks we'd taken, all the small acts of courage that had led to this moment.\n\nAfter the ceremony, one of my former students, a young man named Marcus, came up to me with his family. He introduced me to his parents as \"the teacher who taught me that education and courage go hand in hand.\" His mother hugged me and whispered, \"Thank you for being brave so my son could have choices.\"\n\nThat's when I understood what my grandmother meant about doing what's right. It's not about the recognition - though this old lady appreciates the kind words from the community here on dignity.ink. It's about planting seeds for a harvest you might never see.\n\nI taught for thirty-eight more years, watching integration slowly become normal, watching my students grow up to become doctors and lawyers and teachers themselves. Some of them had children who ended up in my classroom too. That's the real victory - raising a generation that can't imagine a world where people are separated by skin color.\n\nTo the young activists today who are still fighting for justice: the work is hard, the progress is slow, and some days it feels impossible. But every small act of courage matters. Every time you stand up for what's right, you're walking in the footsteps of everyone who came before you, and you're paving the way for everyone who comes after.\n\nKeep walking. Keep fighting. Keep believing that change is possible, because it is. I've seen it happen, one step at a time.\n\n*Dorothy Mae Johnson taught in Alabama public schools for 40 years and continues to speak at schools and community organizations about the civil rights movement. She says her greatest achievement is the thousands of students who learned in her classroom that education and equality go hand in hand.*","src/content/dignity/dorothy-civil-rights.md","7cb4e120d2ce2652",{"html":94,"metadata":95},"\u003Cp>\u003Cem>Editor’s note: This story was originally shared anonymously on nevertell.ink, where it received over 150 votes for promotion. After much encouragement from the community, Dorothy Mae Johnson decided to share her full story and put her name to this important piece of history.\u003C/em>\u003C/p>\n\u003Chr>\n\u003Cp>For sixty years, I kept quiet about my role in the civil rights movement. Not because I was ashamed, but because I was taught that good work doesn’t need recognition. My grandmother used to say, “Do what’s right because it’s right, not because folks are watching.” But at 83, I’ve decided some stories need to be told with names attached, especially when young people today are still fighting for justice.\u003C/p>\n\u003Cp>In 1963, I was a 22-year-old teacher at a segregated elementary school in Montgomery, Alabama. I was making $32 a week teaching 47 children in a classroom meant for 25, using textbooks that were ten years out of date and had already been discarded by the white schools across town.\u003C/p>\n\u003Cp>When Dr. King and the others organized the Montgomery Bus Boycott, I knew I had to be part of it, even though it meant walking four miles to work every morning and four miles home every evening. My principal warned all of us teachers that we could lose our jobs if we were caught participating in “agitation activities.” But how could I teach my students about dignity and self-respect if I wasn’t willing to fight for my own?\u003C/p>\n\u003Cp>The hardest part wasn’t the walking - though Lord knows my feet were screaming by December. The hardest part was the fear. Every morning when I left my apartment, I didn’t know if I’d make it to school safely. There were cars full of angry white men who would drive slowly beside us as we walked, shouting things I won’t repeat, sometimes throwing things.\u003C/p>\n\u003Cp>But we weren’t alone. The solidarity among the Black community during those 381 days was something I’ll never forget. People who owned cars organized carpools. Folks who lived along the walking routes would set up water stations and first aid stops. My neighbor, Mrs. Washington, would make an extra sandwich every morning and press it into my hand as I walked past her house.\u003C/p>\n\u003Cp>One morning in February, it was particularly cold and rainy. I was walking with a group of other teachers and domestic workers when a police car pulled up beside us. My heart started racing - we all knew stories of people being arrested for “loitering” or “disturbing the peace” for simply walking down the street.\u003C/p>\n\u003Cp>But instead of arresting us, the officer rolled down his window and said, “You folks need a ride?” We looked at each other, confused. Then he said, quietly, “My daughter goes to your school, Miss Johnson. She talks about you all the time. Says you’re the best teacher she’s ever had.”\u003C/p>\n\u003Cp>It turned out Officer Williams was one of the few Black police officers in Montgomery, and he’d been secretly helping with the boycott by giving rides when he could do so safely. That day, he drove all six of us teachers to school, and we arrived dry and warm for the first time in months.\u003C/p>\n\u003Cp>After the boycott succeeded, I thought the hardest part was over. I was wrong again. The real work was just beginning. I spent the next five years helping to integrate schools, registering voters, and organizing community meetings. I was arrested three times - once for “trespassing” when I tried to register to vote at the white courthouse, once for “disturbing the peace” during a peaceful protest, and once for “conspiracy” when we organized a voter registration drive.\u003C/p>\n\u003Cp>Each arrest was terrifying, but they were also clarifying. Every time they put me in that cell, I became more certain that what we were doing was not just right, but necessary. My students needed to grow up in a world where their worth wasn’t determined by the color of their skin.\u003C/p>\n\u003Cp>The most meaningful moment came in 1968, when the first integrated class graduated from Montgomery High School. I was teaching at the newly integrated elementary school by then, and several of my former students were in that graduating class. As I watched them walk across that stage - Black and white students together - I thought about all the miles we’d walked, all the risks we’d taken, all the small acts of courage that had led to this moment.\u003C/p>\n\u003Cp>After the ceremony, one of my former students, a young man named Marcus, came up to me with his family. He introduced me to his parents as “the teacher who taught me that education and courage go hand in hand.” His mother hugged me and whispered, “Thank you for being brave so my son could have choices.”\u003C/p>\n\u003Cp>That’s when I understood what my grandmother meant about doing what’s right. It’s not about the recognition - though this old lady appreciates the kind words from the community here on dignity.ink. It’s about planting seeds for a harvest you might never see.\u003C/p>\n\u003Cp>I taught for thirty-eight more years, watching integration slowly become normal, watching my students grow up to become doctors and lawyers and teachers themselves. Some of them had children who ended up in my classroom too. That’s the real victory - raising a generation that can’t imagine a world where people are separated by skin color.\u003C/p>\n\u003Cp>To the young activists today who are still fighting for justice: the work is hard, the progress is slow, and some days it feels impossible. But every small act of courage matters. Every time you stand up for what’s right, you’re walking in the footsteps of everyone who came before you, and you’re paving the way for everyone who comes after.\u003C/p>\n\u003Cp>Keep walking. Keep fighting. Keep believing that change is possible, because it is. I’ve seen it happen, one step at a time.\u003C/p>\n\u003Cp>\u003Cem>Dorothy Mae Johnson taught in Alabama public schools for 40 years and continues to speak at schools and community organizations about the civil rights movement. She says her greatest achievement is the thousands of students who learned in her classroom that education and equality go hand in hand.\u003C/em>\u003C/p>",{"headings":96,"localImagePaths":97,"remoteImagePaths":98,"frontmatter":99,"imagePaths":102},[],[],[],{"title":75,"excerpt":76,"authorName":77,"authorAge":78,"tags":100,"location":83,"dateOfEvent":84,"upvotes":85,"publishedAt":101,"commentCount":89,"isPromoted":86,"originalStoryId":87},[80,18,81,82],["Date","2024-01-08T00:00:00.000Z"],[],"dorothy-civil-rights.md","martha-navy-nurse",{"id":104,"data":106,"body":120,"filePath":121,"digest":122,"rendered":123,"legacyId":133},{"title":107,"excerpt":108,"authorName":109,"authorAge":110,"tags":111,"location":116,"dateOfEvent":117,"upvotes":118,"isPromoted":24,"publishedAt":119,"commentCount":22},"Serving in the Pacific: A Nurse's Story from WWII","Martha Henderson, 98, shares her experiences as a Navy nurse during the Battle of Guadalcanal...","Martha Henderson",98,[112,113,114,115],"WWII","nursing","service","Pacific Theater","Sunset Manor, Portland OR","1943-1945",234,["Date","2024-01-10T00:00:00.000Z"],"I was twenty-two years old when I enlisted as a Navy nurse in 1943. Fresh out of nursing school, I thought I knew what I was getting into. I was wrong about almost everything, but right about the one thing that mattered most: I knew I needed to help.\n\nThey shipped us out to the Pacific Theater, and my first assignment was a hospital ship near Guadalcanal. If you've never been on a hospital ship during wartime, let me tell you - it's organized chaos. We'd get word that casualties were coming in, and suddenly our peaceful floating hospital would transform into the most important place in the world for dozens of young men who just wanted to go home.\n\nThe hardest part wasn't the blood or the wounds - nursing school had prepared me for that. The hardest part was how young they all were. Boys, really, barely old enough to shave, calling out for their mothers in the middle of the night. I was only a few years older than most of them, but I had to be strong for them when they couldn't be strong for themselves.\n\nThere was one boy - I'll call him Tommy, though that wasn't his real name - who came in with shrapnel wounds to his legs. He was conscious and kept apologizing for bleeding on my uniform. Can you imagine? This brave young man, wounded serving his country, apologizing to me for doing my job.\n\nTommy was from a small farm in Iowa, and he told me all about his family's corn fields while I cleaned his wounds. He had a sweetheart back home named Betty, and he showed me her picture so many times I felt like I knew her personally. He was planning to propose when he got back, had already bought the ring and everything.\n\nWe worked on Tommy for hours. The doctors said his legs could be saved, but it would be a long recovery. He'd probably walk with a limp for the rest of his life, but he'd walk. When we told him, he cried with relief. \"Betty won't mind,\" he said. \"She loves me for who I am, not how I walk.\"\n\nThat was my job for two years - being strong for the Tommys of the world, holding their hands when they were scared, celebrating with them when they got good news, and sometimes... sometimes saying goodbye when the good news didn't come.\n\nI learned more about courage on that hospital ship than in all my years before or since. It wasn't the dramatic kind of courage you see in movies. It was quiet courage - the courage to keep going when everything hurts, to smile when you want to cry, to believe in tomorrow when today seems impossible.\n\nWhen the war ended, I came home to Oregon and worked at Portland General Hospital for thirty-seven years. I married a wonderful man named Robert, and we had three children and seven grandchildren. I lived a full, blessed life.\n\nBut I never forgot my Navy family, especially Tommy. About ten years ago, I got a letter from his granddaughter. She'd found my name in some old letters he'd kept. Tommy had passed away in 2018 at the age of ninety-three, surrounded by his family. He'd married his Betty right after the war, just like he planned. They'd had sixty-eight years together.\n\nIn the letter, his granddaughter told me that Tommy used to talk about \"his angel nurse\" who helped him through the darkest time of his life. He never knew my name - military protocol and the chaos of wartime meant we often didn't exchange personal information - but he remembered my kindness.\n\nThat letter sits framed on my nightstand now. Some people might think it's sad that we never reconnected, but I don't see it that way. We were both exactly where we needed to be when we needed to be there. That's what service means - showing up for each other when it matters most, whether you ever see each other again or not.\n\nThe young nurses starting their careers today ask me for advice sometimes. I tell them what I learned on that hospital ship: Your hands can heal, but your heart heals even more. Every patient is someone's Tommy, someone's most precious person. Treat them that way, and you'll never go wrong.\n\n*At 98, Martha still volunteers at the local veterans' hospital every Tuesday, reading to patients and sharing stories from her service.*","src/content/dignity/martha-navy-nurse.md","a8028d61bd9a0f38",{"html":124,"metadata":125},"\u003Cp>I was twenty-two years old when I enlisted as a Navy nurse in 1943. Fresh out of nursing school, I thought I knew what I was getting into. I was wrong about almost everything, but right about the one thing that mattered most: I knew I needed to help.\u003C/p>\n\u003Cp>They shipped us out to the Pacific Theater, and my first assignment was a hospital ship near Guadalcanal. If you’ve never been on a hospital ship during wartime, let me tell you - it’s organized chaos. We’d get word that casualties were coming in, and suddenly our peaceful floating hospital would transform into the most important place in the world for dozens of young men who just wanted to go home.\u003C/p>\n\u003Cp>The hardest part wasn’t the blood or the wounds - nursing school had prepared me for that. The hardest part was how young they all were. Boys, really, barely old enough to shave, calling out for their mothers in the middle of the night. I was only a few years older than most of them, but I had to be strong for them when they couldn’t be strong for themselves.\u003C/p>\n\u003Cp>There was one boy - I’ll call him Tommy, though that wasn’t his real name - who came in with shrapnel wounds to his legs. He was conscious and kept apologizing for bleeding on my uniform. Can you imagine? This brave young man, wounded serving his country, apologizing to me for doing my job.\u003C/p>\n\u003Cp>Tommy was from a small farm in Iowa, and he told me all about his family’s corn fields while I cleaned his wounds. He had a sweetheart back home named Betty, and he showed me her picture so many times I felt like I knew her personally. He was planning to propose when he got back, had already bought the ring and everything.\u003C/p>\n\u003Cp>We worked on Tommy for hours. The doctors said his legs could be saved, but it would be a long recovery. He’d probably walk with a limp for the rest of his life, but he’d walk. When we told him, he cried with relief. “Betty won’t mind,” he said. “She loves me for who I am, not how I walk.”\u003C/p>\n\u003Cp>That was my job for two years - being strong for the Tommys of the world, holding their hands when they were scared, celebrating with them when they got good news, and sometimes… sometimes saying goodbye when the good news didn’t come.\u003C/p>\n\u003Cp>I learned more about courage on that hospital ship than in all my years before or since. It wasn’t the dramatic kind of courage you see in movies. It was quiet courage - the courage to keep going when everything hurts, to smile when you want to cry, to believe in tomorrow when today seems impossible.\u003C/p>\n\u003Cp>When the war ended, I came home to Oregon and worked at Portland General Hospital for thirty-seven years. I married a wonderful man named Robert, and we had three children and seven grandchildren. I lived a full, blessed life.\u003C/p>\n\u003Cp>But I never forgot my Navy family, especially Tommy. About ten years ago, I got a letter from his granddaughter. She’d found my name in some old letters he’d kept. Tommy had passed away in 2018 at the age of ninety-three, surrounded by his family. He’d married his Betty right after the war, just like he planned. They’d had sixty-eight years together.\u003C/p>\n\u003Cp>In the letter, his granddaughter told me that Tommy used to talk about “his angel nurse” who helped him through the darkest time of his life. He never knew my name - military protocol and the chaos of wartime meant we often didn’t exchange personal information - but he remembered my kindness.\u003C/p>\n\u003Cp>That letter sits framed on my nightstand now. Some people might think it’s sad that we never reconnected, but I don’t see it that way. We were both exactly where we needed to be when we needed to be there. That’s what service means - showing up for each other when it matters most, whether you ever see each other again or not.\u003C/p>\n\u003Cp>The young nurses starting their careers today ask me for advice sometimes. I tell them what I learned on that hospital ship: Your hands can heal, but your heart heals even more. Every patient is someone’s Tommy, someone’s most precious person. Treat them that way, and you’ll never go wrong.\u003C/p>\n\u003Cp>\u003Cem>At 98, Martha still volunteers at the local veterans’ hospital every Tuesday, reading to patients and sharing stories from her service.\u003C/em>\u003C/p>",{"headings":126,"localImagePaths":127,"remoteImagePaths":128,"frontmatter":129,"imagePaths":132},[],[],[],{"title":107,"excerpt":108,"authorName":109,"authorAge":110,"tags":130,"location":116,"dateOfEvent":117,"upvotes":118,"publishedAt":131,"commentCount":22,"isPromoted":24},[112,113,114,115],["Date","2024-01-10T00:00:00.000Z"],[],"martha-navy-nurse.md"] \ No newline at end of file diff --git a/.astro/settings.json b/.astro/settings.json new file mode 100644 index 0000000..46cc0b3 --- /dev/null +++ b/.astro/settings.json @@ -0,0 +1,5 @@ +{ + "_variables": { + "lastUpdateCheck": 1755489734681 + } +} \ No newline at end of file diff --git a/.astro/types.d.ts b/.astro/types.d.ts new file mode 100644 index 0000000..03d7cc4 --- /dev/null +++ b/.astro/types.d.ts @@ -0,0 +1,2 @@ +/// +/// \ No newline at end of file diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2107dab --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +node_modules +npm-debug.log +dist +.git +.gitignore +README.md +.env.local +.env.development.local +.env.test.local +.env.production.local \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 0000000..ceb6be5 --- /dev/null +++ b/.env @@ -0,0 +1,21 @@ +# Development Environment Variables +DOMAIN=st.l.supported.systems + +# Site Configuration +PUBLIC_SITE_URL=https://st.l.supported.systems +PUBLIC_API_BASE_URL=https://st.l.supported.systems/api + +# Platform Configuration +PUBLIC_ANONYMOUS_PLATFORM_NAME="Anonymous Storytellers" +PUBLIC_NAMED_PLATFORM_NAME="Named Storytellers" + +# Analytics (optional) +# PUBLIC_ANALYTICS_ID= + +# Contact Information +PUBLIC_CONTACT_EMAIL=stories@st.l.supported.systems + +# Features +PUBLIC_ENABLE_COMMENTS=true +PUBLIC_ENABLE_VOTING=true +PUBLIC_ENABLE_PROMOTION=true \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..55df460 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM node:18-alpine + +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies +RUN npm install + +# Copy source code +COPY . . + +# Expose port +EXPOSE 4321 + +# Start development server +CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..056ca72 --- /dev/null +++ b/README.md @@ -0,0 +1,287 @@ +# story-teller.ink - Astro + Alpine.js Version + +## 🏔️🚀 Lightning Fast Storytelling Platform + +This is the **Astro + Alpine.js** implementation of story-teller.ink - a dual platform connecting seniors through storytelling with **zero-JS-by-default** performance and **15kb total JavaScript**. + +## 🎯 Why Astro + Alpine.js? + +### **Performance for Seniors** +- **~20kb total** per story page (vs 300kb+ React) +- **Instant story loading** - static HTML with no hydration delay +- **15kb Alpine.js** only loads for interactive features +- **Works on ancient devices** - no modern JS features required + +### **Accessibility First** +- **HTML-first** - stories load immediately, work with JS disabled +- **Progressive enhancement** - Alpine adds interactivity gracefully +- **Screen reader perfect** - semantic HTML from the start +- **High contrast & reduced motion** support built-in + +### **Content Management** +- **Markdown stories** - easy to write and maintain +- **Content collections** - organized by platform (nevertell/dignity) +- **Static generation** - stories become permanent web pages +- **Type-safe frontmatter** with Zod validation + +## 🏗️ Architecture Overview + +``` +src/ +├── content/ +│ ├── nevertell/ # Anonymous stories (markdown) +│ │ ├── elvis-concert-1956.md +│ │ └── college-roommate-prank.md +│ └── dignity/ # Named stories (markdown) +│ ├── martha-navy-nurse.md +│ └── dorothy-civil-rights.md +├── pages/ +│ ├── index.astro # Main landing page +│ ├── nevertell/ +│ │ ├── index.astro # Anonymous story list + Alpine filtering +│ │ └── [slug].astro # Static story + Alpine comments +│ └── dignity/ +│ ├── index.astro # Named story list +│ └── [slug].astro # Static story + Alpine interactions +├── layouts/ +│ ├── BaseLayout.astro # Global Alpine.js setup +│ ├── NevertellLayout.astro +│ └── DignityLayout.astro +└── styles/ + └── global.css # Tailwind + accessibility styles +``` + +## 🌟 Key Features + +### **Dual Platform Architecture** +- **Anonymous Storytellers** 🤫 - Anonymous stories with mischievous tone +- **Named Storytellers** 🏆 - Named stories with respectful tone +- **Cross-platform promotion** - community can encourage anonymous→named + +### **Static + Interactive** +- **Static story pages** load instantly +- **Alpine.js islands** for voting, comments, filtering +- **Progressive enhancement** - works without JavaScript +- **Real-time interactions** where needed + +### **Senior-Optimized** +- **Large fonts** (18px base, scalable to 20px+) +- **44px minimum touch targets** for mobile +- **High contrast mode** support +- **Keyboard navigation** optimized +- **Reduced motion** preferences honored + +## 🚀 Getting Started + +### Prerequisites +- Node.js 18+ +- npm or yarn + +### Installation +```bash +cd story-bridge-astro +npm install +``` + +### Development +```bash +npm run dev +# Visit http://localhost:4321 +``` + +### Build for Production +```bash +npm run build +npm run preview +``` + +## 📖 Content Management + +### Adding New Stories + +#### Anonymous Story +```markdown +--- +# src/content/nevertell/new-story.md +title: "The Time I..." +excerpt: "Brief description..." +tags: ["1960s", "college", "pranks"] +location: "Senior Center, City State" +dateOfEvent: "Summer 1965" +upvotes: 0 +promotionVotes: 0 +publishedAt: 2024-01-20T00:00:00.000Z +commentCount: 0 +--- + +Your amazing story content here in beautiful markdown... +``` + +#### Named Story +```markdown +--- +# src/content/dignity/new-story.md +title: "Hero's Journey" +excerpt: "Brief description..." +authorName: "John Smith" +authorAge: 87 +tags: ["WWII", "service", "heroism"] +location: "Veterans Home, City State" +dateOfEvent: "1943-1945" +upvotes: 0 +publishedAt: 2024-01-20T00:00:00.000Z +commentCount: 0 +--- + +Their incredible story of service and sacrifice... +``` + +### Content Validation +All story frontmatter is validated with Zod schemas in `src/content/config.ts`. + +## ⚡ Performance Metrics + +### Page Load Sizes +- **Main page**: ~25kb (HTML + CSS + 15kb Alpine) +- **Story pages**: ~20kb (mostly static HTML content) +- **Story list**: ~30kb (static HTML + Alpine filtering) + +### Performance Benefits +- **0ms hydration** - stories load as static HTML +- **Instant navigation** - no client-side routing delays +- **Offline reading** - cached HTML works without connection +- **SEO perfect** - fully rendered HTML for search engines + +## 🎨 Styling & Themes + +### Platform-Specific Themes +```css +/* Anonymous Storytellers - playful, mischievous */ +nevertell: { + primary: '#6366f1', // indigo + secondary: '#ec4899', // pink + accent: '#f59e0b', // amber +} + +/* Named Storytellers - warm, respectful */ +dignity: { + primary: '#059669', // emerald + secondary: '#dc2626', // red + accent: '#d97706', // amber +} +``` + +### Accessibility Features +- **Large fonts** with scalable sizing +- **High contrast** mode support +- **Reduced motion** preferences +- **Focus indicators** for keyboard navigation +- **Screen reader** optimized markup + +## 🌐 Deployment + +### Vercel (Recommended) +```bash +npm install -g vercel +vercel --prod +``` + +### Netlify +```bash +npm run build +# Upload dist/ folder to Netlify +``` + +### Static Hosting +```bash +npm run build +# Serve dist/ folder from any static host +``` + +### Domain Configuration +Configure `story-teller.ink` for hosting. The platform handles both anonymous and named storytellers on the same domain with different routes. + +## 🔧 Alpine.js Features + +### Global Stores +```javascript +// Utility functions available everywhere +Alpine.store('utils', { + formatDate(dateString), + formatRelativeTime(dateString), + truncateText(text, maxLength) +}); + +// API functions for voting and comments +Alpine.store('api', { + vote(storyId, type), + submitComment(storyId, content, authorName), + loadComments(storyId) +}); +``` + +### Interactive Components +- **Story filtering** with real-time search and tag selection +- **Voting systems** for upvotes and promotions +- **Comment systems** with threaded discussions +- **Form validation** and submission handling + +## 🎯 Mission Statement + +We're building bridges between the generation that invented rock & roll and the generation that invented the internet. + +### The Story Bridge Effect +1. **Stories flow out** (resident → internet) +2. **Connections flow back** (internet → resident) +3. **Social energy flows within** (resident ↔ other residents) + +## 🤝 Contributing + +### Adding Features +1. Static content goes in `src/pages/` and `src/content/` +2. Interactive features use Alpine.js data functions +3. Maintain accessibility standards +4. Test on mobile and with keyboard navigation + +### Content Guidelines +- **Anonymous stories** should be fun, engaging, authentic +- **Named stories** should be respectful, honoring, dignified +- **All stories** must have proper frontmatter and tags +- **Excerpts** should be compelling and draw readers in + +## 📊 Analytics & Monitoring + +### Metrics to Track +- **Story reading time** (engagement) +- **Comment engagement** (community building) +- **Cross-platform promotions** (success indicator) +- **Mobile vs desktop** usage (senior preferences) +- **Accessibility feature** usage + +### Success Indicators +- Stories being shared outside the platform +- Comments creating real connections +- Anonymous stories being promoted to named storytellers +- Seniors discussing stories in their facilities + +## 🔮 Future Enhancements + +### Planned Features +- **RSS feeds** for story subscriptions +- **Story search** with full-text indexing +- **Related stories** based on tags and themes +- **Email notifications** for story responses +- **Print-friendly** story formats +- **Audio narration** for accessibility + +### Technical Improvements +- **Progressive Web App** features +- **Offline story caching** +- **Image optimization** for story photos +- **CDN integration** for global performance + +--- + +Built with ❤️ for the generation that has the best stories to tell. + +**Performance**: Astro ⚡ **Interactivity**: Alpine.js 🏔️ **Accessibility**: Senior-first ♿ \ No newline at end of file diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..3d8df9e --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,31 @@ +import { defineConfig } from 'astro/config'; +import tailwind from '@astrojs/tailwind'; + +export default defineConfig({ + integrations: [tailwind()], + site: process.env.PUBLIC_SITE_URL || 'https://st.l.supported.systems', + output: 'static', + + server: { + host: '0.0.0.0', + port: 4321 + }, + + // Handle domain routing for both .ink domains + vite: { + define: { + global: 'globalThis', + }, + server: { + host: '0.0.0.0', + allowedHosts: [ + process.env.DOMAIN || 'st.l.supported.systems', + 'localhost', + '127.0.0.1' + ], + fs: { + allow: ['..'] + } + } + } +}); \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..130ed10 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,32 @@ +version: '3.8' + +services: + story-teller: + build: . + container_name: story-teller-app + environment: + - NODE_ENV=development + - DOMAIN=${DOMAIN} + - PUBLIC_SITE_URL=${PUBLIC_SITE_URL} + - PUBLIC_API_BASE_URL=${PUBLIC_API_BASE_URL} + - PUBLIC_ANONYMOUS_PLATFORM_NAME=${PUBLIC_ANONYMOUS_PLATFORM_NAME} + - PUBLIC_NAMED_PLATFORM_NAME=${PUBLIC_NAMED_PLATFORM_NAME} + - PUBLIC_CONTACT_EMAIL=${PUBLIC_CONTACT_EMAIL} + - PUBLIC_ENABLE_COMMENTS=${PUBLIC_ENABLE_COMMENTS} + - PUBLIC_ENABLE_VOTING=${PUBLIC_ENABLE_VOTING} + - PUBLIC_ENABLE_PROMOTION=${PUBLIC_ENABLE_PROMOTION} + labels: + caddy: ${DOMAIN} + caddy.reverse_proxy: "{{upstreams 4321}}" + networks: + - caddy + volumes: + - .:/app + - /app/node_modules + expose: + - "4321" + restart: unless-stopped + +networks: + caddy: + external: true \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..9f98085 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "story-bridge-astro", + "type": "module", + "version": "1.0.0", + "description": "Astro + Alpine.js version of The Story Bridge Project", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "astro": "^5.13.2", + "@astrojs/tailwind": "^5.1.3", + "tailwindcss": "^3.4.0", + "alpinejs": "^3.14.3" + }, + "devDependencies": { + "@types/node": "^20.12.0", + "typescript": "^5.4.0" + } +} \ No newline at end of file diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..81a538f --- /dev/null +++ b/setup.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Create caddy network if it doesn't exist +docker network ls | grep caddy > /dev/null || docker network create caddy + +echo "Caddy network ready!" +echo "Now you can run: docker compose up -d" +echo "Your app will be available at: https://${DOMAIN:-st.l.supported.systems}" \ No newline at end of file diff --git a/src/content/config.ts b/src/content/config.ts new file mode 100644 index 0000000..71e69d6 --- /dev/null +++ b/src/content/config.ts @@ -0,0 +1,40 @@ +import { defineCollection, z } from 'astro:content'; + +const nevertellStories = defineCollection({ + type: 'content', + schema: z.object({ + title: z.string(), + excerpt: z.string().optional(), + tags: z.array(z.string()).default([]), + location: z.string().optional(), + dateOfEvent: z.string().optional(), + upvotes: z.number().default(0), + promotionVotes: z.number().default(0), + isPromoted: z.boolean().default(false), + publishedAt: z.date(), + commentCount: z.number().default(0), + }), +}); + +const dignityStories = defineCollection({ + type: 'content', + schema: z.object({ + title: z.string(), + excerpt: z.string().optional(), + authorName: z.string(), + authorAge: z.number().optional(), + tags: z.array(z.string()).default([]), + location: z.string().optional(), + dateOfEvent: z.string().optional(), + upvotes: z.number().default(0), + isPromoted: z.boolean().default(false), + originalStoryId: z.string().optional(), + publishedAt: z.date(), + commentCount: z.number().default(0), + }), +}); + +export const collections = { + 'nevertell': nevertellStories, + 'dignity': dignityStories, +}; \ No newline at end of file diff --git a/src/content/dignity/dorothy-civil-rights.md b/src/content/dignity/dorothy-civil-rights.md new file mode 100644 index 0000000..dd50704 --- /dev/null +++ b/src/content/dignity/dorothy-civil-rights.md @@ -0,0 +1,52 @@ +--- +title: "From Anonymous to Proud: My Journey Fighting for Civil Rights" +excerpt: "Originally shared anonymously on nevertell.ink, Dorothy Mae Johnson decided to tell her full story..." +authorName: "Dorothy Mae Johnson" +authorAge: 83 +tags: ["civil rights", "1960s", "activism", "courage"] +location: "Golden Years Community, Atlanta GA" +dateOfEvent: "1963-1968" +upvotes: 312 +publishedAt: 2024-01-08T00:00:00.000Z +commentCount: 143 +isPromoted: true +originalStoryId: "dorothy-bus-boycott" +--- + +*Editor's note: This story was originally shared anonymously on nevertell.ink, where it received over 150 votes for promotion. After much encouragement from the community, Dorothy Mae Johnson decided to share her full story and put her name to this important piece of history.* + +--- + +For sixty years, I kept quiet about my role in the civil rights movement. Not because I was ashamed, but because I was taught that good work doesn't need recognition. My grandmother used to say, "Do what's right because it's right, not because folks are watching." But at 83, I've decided some stories need to be told with names attached, especially when young people today are still fighting for justice. + +In 1963, I was a 22-year-old teacher at a segregated elementary school in Montgomery, Alabama. I was making $32 a week teaching 47 children in a classroom meant for 25, using textbooks that were ten years out of date and had already been discarded by the white schools across town. + +When Dr. King and the others organized the Montgomery Bus Boycott, I knew I had to be part of it, even though it meant walking four miles to work every morning and four miles home every evening. My principal warned all of us teachers that we could lose our jobs if we were caught participating in "agitation activities." But how could I teach my students about dignity and self-respect if I wasn't willing to fight for my own? + +The hardest part wasn't the walking - though Lord knows my feet were screaming by December. The hardest part was the fear. Every morning when I left my apartment, I didn't know if I'd make it to school safely. There were cars full of angry white men who would drive slowly beside us as we walked, shouting things I won't repeat, sometimes throwing things. + +But we weren't alone. The solidarity among the Black community during those 381 days was something I'll never forget. People who owned cars organized carpools. Folks who lived along the walking routes would set up water stations and first aid stops. My neighbor, Mrs. Washington, would make an extra sandwich every morning and press it into my hand as I walked past her house. + +One morning in February, it was particularly cold and rainy. I was walking with a group of other teachers and domestic workers when a police car pulled up beside us. My heart started racing - we all knew stories of people being arrested for "loitering" or "disturbing the peace" for simply walking down the street. + +But instead of arresting us, the officer rolled down his window and said, "You folks need a ride?" We looked at each other, confused. Then he said, quietly, "My daughter goes to your school, Miss Johnson. She talks about you all the time. Says you're the best teacher she's ever had." + +It turned out Officer Williams was one of the few Black police officers in Montgomery, and he'd been secretly helping with the boycott by giving rides when he could do so safely. That day, he drove all six of us teachers to school, and we arrived dry and warm for the first time in months. + +After the boycott succeeded, I thought the hardest part was over. I was wrong again. The real work was just beginning. I spent the next five years helping to integrate schools, registering voters, and organizing community meetings. I was arrested three times - once for "trespassing" when I tried to register to vote at the white courthouse, once for "disturbing the peace" during a peaceful protest, and once for "conspiracy" when we organized a voter registration drive. + +Each arrest was terrifying, but they were also clarifying. Every time they put me in that cell, I became more certain that what we were doing was not just right, but necessary. My students needed to grow up in a world where their worth wasn't determined by the color of their skin. + +The most meaningful moment came in 1968, when the first integrated class graduated from Montgomery High School. I was teaching at the newly integrated elementary school by then, and several of my former students were in that graduating class. As I watched them walk across that stage - Black and white students together - I thought about all the miles we'd walked, all the risks we'd taken, all the small acts of courage that had led to this moment. + +After the ceremony, one of my former students, a young man named Marcus, came up to me with his family. He introduced me to his parents as "the teacher who taught me that education and courage go hand in hand." His mother hugged me and whispered, "Thank you for being brave so my son could have choices." + +That's when I understood what my grandmother meant about doing what's right. It's not about the recognition - though this old lady appreciates the kind words from the community here on dignity.ink. It's about planting seeds for a harvest you might never see. + +I taught for thirty-eight more years, watching integration slowly become normal, watching my students grow up to become doctors and lawyers and teachers themselves. Some of them had children who ended up in my classroom too. That's the real victory - raising a generation that can't imagine a world where people are separated by skin color. + +To the young activists today who are still fighting for justice: the work is hard, the progress is slow, and some days it feels impossible. But every small act of courage matters. Every time you stand up for what's right, you're walking in the footsteps of everyone who came before you, and you're paving the way for everyone who comes after. + +Keep walking. Keep fighting. Keep believing that change is possible, because it is. I've seen it happen, one step at a time. + +*Dorothy Mae Johnson taught in Alabama public schools for 40 years and continues to speak at schools and community organizations about the civil rights movement. She says her greatest achievement is the thousands of students who learned in her classroom that education and equality go hand in hand.* \ No newline at end of file diff --git a/src/content/dignity/martha-navy-nurse.md b/src/content/dignity/martha-navy-nurse.md new file mode 100644 index 0000000..e9acc3a --- /dev/null +++ b/src/content/dignity/martha-navy-nurse.md @@ -0,0 +1,41 @@ +--- +title: "Serving in the Pacific: A Nurse's Story from WWII" +excerpt: "Martha Henderson, 98, shares her experiences as a Navy nurse during the Battle of Guadalcanal..." +authorName: "Martha Henderson" +authorAge: 98 +tags: ["WWII", "nursing", "service", "Pacific Theater"] +location: "Sunset Manor, Portland OR" +dateOfEvent: "1943-1945" +upvotes: 234 +publishedAt: 2024-01-10T00:00:00.000Z +commentCount: 89 +isPromoted: false +--- + +I was twenty-two years old when I enlisted as a Navy nurse in 1943. Fresh out of nursing school, I thought I knew what I was getting into. I was wrong about almost everything, but right about the one thing that mattered most: I knew I needed to help. + +They shipped us out to the Pacific Theater, and my first assignment was a hospital ship near Guadalcanal. If you've never been on a hospital ship during wartime, let me tell you - it's organized chaos. We'd get word that casualties were coming in, and suddenly our peaceful floating hospital would transform into the most important place in the world for dozens of young men who just wanted to go home. + +The hardest part wasn't the blood or the wounds - nursing school had prepared me for that. The hardest part was how young they all were. Boys, really, barely old enough to shave, calling out for their mothers in the middle of the night. I was only a few years older than most of them, but I had to be strong for them when they couldn't be strong for themselves. + +There was one boy - I'll call him Tommy, though that wasn't his real name - who came in with shrapnel wounds to his legs. He was conscious and kept apologizing for bleeding on my uniform. Can you imagine? This brave young man, wounded serving his country, apologizing to me for doing my job. + +Tommy was from a small farm in Iowa, and he told me all about his family's corn fields while I cleaned his wounds. He had a sweetheart back home named Betty, and he showed me her picture so many times I felt like I knew her personally. He was planning to propose when he got back, had already bought the ring and everything. + +We worked on Tommy for hours. The doctors said his legs could be saved, but it would be a long recovery. He'd probably walk with a limp for the rest of his life, but he'd walk. When we told him, he cried with relief. "Betty won't mind," he said. "She loves me for who I am, not how I walk." + +That was my job for two years - being strong for the Tommys of the world, holding their hands when they were scared, celebrating with them when they got good news, and sometimes... sometimes saying goodbye when the good news didn't come. + +I learned more about courage on that hospital ship than in all my years before or since. It wasn't the dramatic kind of courage you see in movies. It was quiet courage - the courage to keep going when everything hurts, to smile when you want to cry, to believe in tomorrow when today seems impossible. + +When the war ended, I came home to Oregon and worked at Portland General Hospital for thirty-seven years. I married a wonderful man named Robert, and we had three children and seven grandchildren. I lived a full, blessed life. + +But I never forgot my Navy family, especially Tommy. About ten years ago, I got a letter from his granddaughter. She'd found my name in some old letters he'd kept. Tommy had passed away in 2018 at the age of ninety-three, surrounded by his family. He'd married his Betty right after the war, just like he planned. They'd had sixty-eight years together. + +In the letter, his granddaughter told me that Tommy used to talk about "his angel nurse" who helped him through the darkest time of his life. He never knew my name - military protocol and the chaos of wartime meant we often didn't exchange personal information - but he remembered my kindness. + +That letter sits framed on my nightstand now. Some people might think it's sad that we never reconnected, but I don't see it that way. We were both exactly where we needed to be when we needed to be there. That's what service means - showing up for each other when it matters most, whether you ever see each other again or not. + +The young nurses starting their careers today ask me for advice sometimes. I tell them what I learned on that hospital ship: Your hands can heal, but your heart heals even more. Every patient is someone's Tommy, someone's most precious person. Treat them that way, and you'll never go wrong. + +*At 98, Martha still volunteers at the local veterans' hospital every Tuesday, reading to patients and sharing stories from her service.* \ No newline at end of file diff --git a/src/content/nevertell/college-roommate-prank.md b/src/content/nevertell/college-roommate-prank.md new file mode 100644 index 0000000..24a042e --- /dev/null +++ b/src/content/nevertell/college-roommate-prank.md @@ -0,0 +1,39 @@ +--- +title: "College Roommate Shenanigans That Almost Got Us Expelled" +excerpt: "It involved a dean's car, three chickens, and a very confused security guard..." +tags: ["college", "1960s", "pranks"] +location: "Golden Years Community, Boston MA" +dateOfEvent: "Spring 1963" +upvotes: 89 +promotionVotes: 12 +publishedAt: 2024-01-12T00:00:00.000Z +commentCount: 32 +--- + +Spring of 1963, my roommate Jimmy and I were facing our final exams, and we were stressed out of our minds. We'd been cooped up in the library for weeks, and I think we both went a little stir-crazy. + +It started as a joke when we saw Dean Morrison's brand new Cadillac parked in his reserved spot. Jimmy said, "Wouldn't it be funny if..." and I should have stopped him right there, but I didn't. + +The plan was ridiculous: we were going to "redecorate" his car. Nothing permanent, just something that would make him scratch his head. We bought three live chickens from a farmer outside town (don't ask me how we got them back to campus), and a bunch of balloons. + +At 2 AM, we snuck out to the parking lot. The security guard was making his rounds, so we had to time it perfectly. We tied the balloons to the car's antenna and bumpers, then - and this is where it gets really stupid - we put the chickens inside the car. + +How did we get into a locked car? Jimmy's dad was a locksmith, and he'd taught Jimmy a thing or two. We figured the chickens would just sit there looking confused, we'd get a laugh, and then we'd let them out before morning. + +What we didn't account for was that chickens don't just sit quietly. By morning, those birds had completely destroyed the interior of Dean Morrison's brand new Cadillac. Feathers everywhere, you-know-what on every surface, and somehow one of them had figured out how to honk the horn repeatedly. + +The security guard found the car at 6 AM with balloons bobbing in the breeze and chickens raising absolute hell inside. Half the campus came out to see what the commotion was about. + +Dean Morrison was... not amused. Jimmy and I were called into his office that afternoon, where he sat behind his desk with feathers still stuck in his hair from trying to retrieve his car keys from the backseat. + +"Gentlemen," he said, "I have two questions. First: why chickens? Second: how did you plan to explain this to my wife?" + +We told him the truth - that we were stressed about exams and thought it would be funny. He stared at us for what felt like an hour, then started laughing so hard he couldn't breathe. + +Turns out, he and his fraternity brothers had done something similar to the previous dean twenty years earlier, except with a cow (don't ask me how they got a cow up to the third floor of the administration building). + +Our punishment? We had to wash and detail his car every week for the rest of the semester, and we had to take the chickens back to the farmer and explain to him why we were returning slightly traumatized poultry. + +Jimmy and I graduated that spring, and Dean Morrison gave us each a small rubber chicken with our diplomas. I kept mine for sixty years until my granddaughter finally convinced me to tell her this story. Now it sits on her desk at college, and I suspect she's planning her own shenanigans. + +Some traditions never die, they just get passed down to the next generation of troublemakers. \ No newline at end of file diff --git a/src/content/nevertell/elvis-concert-1956.md b/src/content/nevertell/elvis-concert-1956.md new file mode 100644 index 0000000..2aef323 --- /dev/null +++ b/src/content/nevertell/elvis-concert-1956.md @@ -0,0 +1,29 @@ +--- +title: "The Time I Snuck Out to See Elvis" +excerpt: "My parents thought I was at Bible study, but I was actually..." +tags: ["1950s", "music", "rebellion"] +location: "Sunset Manor, Memphis TN" +dateOfEvent: "Summer 1956" +upvotes: 127 +promotionVotes: 23 +publishedAt: 2024-01-15T00:00:00.000Z +commentCount: 45 +--- + +My parents thought I was at Bible study, but I was actually waiting outside the back door of the community center where Elvis was performing his first paid concert in our little town. + +I was seventeen and had never disobeyed my parents before, but something about that music just called to me. When I heard he was coming to town, I knew I had to see him, even if it meant lying to my family. + +The plan was simple: tell my parents I was going to Wednesday night Bible study (which I never missed), but instead sneak around to the back of the community center where the colored folks had to enter. I figured if I stood there, I might be able to hear the music. + +What I didn't expect was for Elvis himself to come out that back door during his break! He was just a young man then, probably only a few years older than me, and when he saw me standing there in my church dress, he smiled and said, "Well hello there, darlin'. You waitin' for someone?" + +I was so nervous I could barely speak, but I managed to tell him I just wanted to hear the music. He laughed - not mean, but kind - and said, "Well shoot, come on in then. Music's for everyone." + +And that's how I ended up dancing to Elvis Presley before anyone knew who he was, in a room full of people my parents would have been scandalized to know I was mixing with. But you know what? It was the most alive I'd ever felt. + +When I got home three hours later, I told my parents Bible study ran long because we were discussing "joyful noise unto the Lord." It wasn't even a lie, really. + +I never told them the truth, even after Elvis became famous. But every time I heard "That's All Right" on the radio, I'd smile and remember the night I learned that sometimes the most important lessons come from the most unexpected places. + +*[This story received 23 votes for promotion to dignity.ink, but the author chose to keep it anonymous]* \ No newline at end of file diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro new file mode 100644 index 0000000..75eed9b --- /dev/null +++ b/src/layouts/BaseLayout.astro @@ -0,0 +1,181 @@ +--- +export interface Props { + title: string; + description?: string; + platform?: 'nevertell' | 'dignity' | 'main'; +} + +const { title, description = "The Story Bridge Project - Connecting generations through storytelling", platform = 'main' } = Astro.props; + +// Platform-specific styling +const platformStyles = { + nevertell: { + bgClass: 'bg-nevertell-background', + primaryColor: 'nevertell-primary', + textColor: 'nevertell-text' + }, + dignity: { + bgClass: 'bg-dignity-background', + primaryColor: 'dignity-primary', + textColor: 'dignity-text' + }, + main: { + bgClass: 'bg-gradient-to-br from-indigo-50 to-emerald-50', + primaryColor: 'gray-800', + textColor: 'gray-900' + } +}; + +const styles = platformStyles[platform]; +--- + + + + + + + + {title} + + + + + + + + + + + + + + + + + Skip to main content + + +
+ +
+ + + + + \ No newline at end of file diff --git a/src/layouts/DignityLayout.astro b/src/layouts/DignityLayout.astro new file mode 100644 index 0000000..f5aabf9 --- /dev/null +++ b/src/layouts/DignityLayout.astro @@ -0,0 +1,100 @@ +--- +import BaseLayout from './BaseLayout.astro'; + +export interface Props { + title: string; + description?: string; +} + +const { title, description } = Astro.props; +--- + + +
+ +
+ +
+ +
+ + +
\ No newline at end of file diff --git a/src/layouts/NevertellLayout.astro b/src/layouts/NevertellLayout.astro new file mode 100644 index 0000000..c4e6441 --- /dev/null +++ b/src/layouts/NevertellLayout.astro @@ -0,0 +1,94 @@ +--- +import BaseLayout from './BaseLayout.astro'; + +export interface Props { + title: string; + description?: string; +} + +const { title, description } = Astro.props; +--- + + +
+ +
+ +
+ +
+ + +
\ No newline at end of file diff --git a/src/pages/dignity/[slug].astro b/src/pages/dignity/[slug].astro new file mode 100644 index 0000000..f049b92 --- /dev/null +++ b/src/pages/dignity/[slug].astro @@ -0,0 +1,285 @@ +--- +import { getCollection } from 'astro:content'; +import DignityLayout from '../../layouts/DignityLayout.astro'; + +export async function getStaticPaths() { + const stories = await getCollection('dignity'); + return stories.map((story) => ({ + params: { slug: story.slug }, + props: story, + })); +} + +const story = Astro.props; +const { Content } = await story.render(); +--- + + +
+
+ {/* Story Header */} +
+ + ← Back to Stories + + + {story.data.isPromoted && ( +
+

+ ✨ This story was promoted from nevertell.ink after community encouragement! +

+
+ )} + +

+ {story.data.title} +

+ +
+ + {story.data.authorName} + + {story.data.authorAge && ( + Age {story.data.authorAge} + )} + {story.data.location && ( + 📍 {story.data.location} + )} + {story.data.dateOfEvent && ( + 📅 Period: {story.data.dateOfEvent} + )} + 📝 {story.data.publishedAt.toLocaleDateString()} +
+ + {story.data.tags.length > 0 && ( +
+ {story.data.tags.map(tag => ( + + #{tag} + + ))} +
+ )} +
+ + {/* Story Content */} +
+
+ +
+
+ + {/* Interactive Honor Section with Alpine.js */} +
+
+
+ + +
+ 💬 + {story.data.commentCount} reflections +
+
+ + +
+ +
+

+
+
+ + {/* Comments Section with Alpine.js */} +
+

+ Community Reflections () +

+ + {/* Comment Form */} +
+
+
+ +
+
+ +
+
+
+

+ Share how this story touches you or reminds you of someone special in your life. +

+ +
+
+ + {/* Comments List */} +
+
+
🙏
+

Be the first to honor this story with your reflection.

+
+ +
+
+

Loading reflections...

+
+ + +
+
+ + {/* Author Recognition */} +
+

+ Thank You, {story.data.authorName} +

+

+ Your story has been preserved as part of our permanent honor roll. + It will inspire future generations and ensure your experiences are never forgotten. +

+ +
+
+
+ + +
\ No newline at end of file diff --git a/src/pages/dignity/index.astro b/src/pages/dignity/index.astro new file mode 100644 index 0000000..b42aecc --- /dev/null +++ b/src/pages/dignity/index.astro @@ -0,0 +1,207 @@ +--- +import { getCollection } from 'astro:content'; +import DignityLayout from '../../layouts/DignityLayout.astro'; + +const stories = await getCollection('dignity'); +const sortedStories = stories.sort((a, b) => b.data.publishedAt.getTime() - a.data.publishedAt.getTime()); +--- + + +
+ {/* Hero Section */} +
+

+ Stories They Wish the World Would Remember +

+

+ Named accounts of heroism, service, love, and the moments that defined a generation. + These are the stories of ordinary people who did extraordinary things. +

+

+ Warm reverence, pull up a chair... +

+
+ + {/* Featured Stories */} +
+
+

Featured Stories

+ + Share Your Story + +
+ +
+ {sortedStories.map(async (story) => { + const { Content } = await story.render(); + return ( +
+ {story.data.isPromoted && ( +
+

+ ✨ This story was promoted from nevertell.ink after community encouragement! +

+
+ )} + + + +
+ {story.data.authorName} + {story.data.authorAge && ( + <> + + Age {story.data.authorAge} + + )} + {story.data.location && ( + <> + + {story.data.location} + + )} +
+ +

+ {story.data.excerpt || (await story.render()).remarkPluginFrontmatter?.excerpt} +

+ +
+ {story.data.tags.map(tag => ( + + #{tag} + + ))} +
+ +
+
+ + 🙏 + {story.data.upvotes} honors + + + 💬 + {story.data.commentCount} reflections + +
+ + + Read Full Story → + +
+
+ ); + })} +
+
+ + {/* Categories Section */} +
+

+ Honor Stories by Theme +

+ +
+
+
🎖️
+

Heroes & Service

+

+ Stories of military service, community leadership, and everyday heroism +

+ + Explore Heroes → + +
+ +
+
❤️
+

Love & Family

+

+ Enduring love stories, family traditions, and generational wisdom +

+ + Explore Love Stories → + +
+ +
+
🏗️
+

Builders & Pioneers

+

+ Those who built communities, broke barriers, and paved the way +

+ + Explore Pioneers → + +
+
+
+ + {/* Recent Additions */} +
+

+ Recent Additions to Our Honor Roll +

+ +
+ {sortedStories.slice(0, 3).map(story => ( +
+
+

{story.data.authorName}

+

+ {story.data.authorAge && `Age ${story.data.authorAge} • `} + {story.data.tags[0] && `${story.data.tags[0].charAt(0).toUpperCase() + story.data.tags[0].slice(1)}`} +

+

{story.data.location}

+
+ + Read Their Story → + +
+ ))} +
+
+ + {/* Call to Action */} +
+

+ Share a Story That Deserves Recognition +

+

+ Know someone whose story should be honored? Help them share their legacy with the world. +

+ +
+
+
\ No newline at end of file diff --git a/src/pages/index.astro b/src/pages/index.astro new file mode 100644 index 0000000..88daa9b --- /dev/null +++ b/src/pages/index.astro @@ -0,0 +1,196 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +--- + + +
+
+
+

+ story-teller.ink +

+

+ Where storytellers gather. Every senior is a storyteller with experiences worth sharing - + the whispered tales and the celebrated legacies. +

+

+ "Empowering the generation that lived history to share their stories with the world." +

+
+ +
+ +
+
+
🎭
+

+ Anonymous Storytellers +

+

+ "Between you and me..." +

+

+ For the storytellers who want to share their most adventurous tales + without revealing their identity. Wild adventures, secret rebellions, + and the stories that make you say "I can't believe I did that!" +

+
+ +
+
+ 🎭 + Anonymous storytelling +
+
+ + Mischievous wink, playful tone +
+
+ 💬 + Community encouragement +
+
+ + + Share Anonymously + +
+ + +
+
+
🏆
+

+ Named Storytellers +

+

+ "Pull up a chair..." +

+

+ For the storytellers who want recognition for their experiences. + Stories of heroism, service, love, and the moments that shaped + a generation - with proper credit to the storyteller. +

+
+ +
+
+ 🏆 + Named storytelling +
+
+ 🎖️ + Warm reverence, respectful tone +
+
+ 📚 + Legacy preservation +
+
+ + + Share With Recognition + +
+
+ +
+

+ Empowering Storytellers +

+
+
+
🎙️
+

Every Senior is a Storyteller

+

+ We visit senior communities to help storytellers share their incredible experiences +

+
+
+
🌐
+

Connect Across Generations

+

+ Stories bridge the gap between those who lived history and those learning from it +

+
+
+
💝
+

Preserve & Honor

+

+ Each story becomes part of our permanent archive, honoring the storyteller's legacy +

+
+
+
+ + +
+

+ Celebrating Our Storytellers +

+
+
+
+
Anonymous Storytellers
+
+
+
+
Named Storytellers
+
+
+
+
Cross-Generational Connections
+
+
+
+
Senior Communities
+
+
+
+
+
+ + +
\ No newline at end of file diff --git a/src/pages/nevertell/[slug].astro b/src/pages/nevertell/[slug].astro new file mode 100644 index 0000000..5098d57 --- /dev/null +++ b/src/pages/nevertell/[slug].astro @@ -0,0 +1,277 @@ +--- +import { getCollection } from 'astro:content'; +import NevertellLayout from '../../layouts/NevertellLayout.astro'; + +export async function getStaticPaths() { + const stories = await getCollection('nevertell'); + return stories.map((story) => ({ + params: { slug: story.slug }, + props: story, + })); +} + +const story = Astro.props; +const { Content } = await story.render(); +--- + + +
+
+ {/* Story Header */} +
+ + ← Back to Stories + + +

+ {story.data.title} +

+ +
+ Anonymous storyteller + {story.data.location && ( + 📍 {story.data.location} + )} + {story.data.dateOfEvent && ( + 📅 {story.data.dateOfEvent} + )} + 📝 {story.data.publishedAt.toLocaleDateString()} +
+ + {story.data.tags.length > 0 && ( +
+ {story.data.tags.map(tag => ( + + #{tag} + + ))} +
+ )} +
+ + {/* Story Content */} +
+
+ +
+
+ + {/* Interactive Voting Section with Alpine.js */} +
+
+
+ + +
+ 💬 + {story.data.commentCount} comments +
+
+ +
+ +
+ +
+

+ people think this deserves recognition! +

+ + Encourage author to go public → + +
+
+ +
+

+
+
+ + {/* Comments Section with Alpine.js */} +
+

+ Comments () +

+ + {/* Comment Form */} +
+
+ +
+
+

+ Comments are anonymous by default. Feel free to share your own similar experiences! +

+ +
+
+ + {/* Comments List */} +
+
+
💭
+

Be the first to comment on this story!

+
+ +
+
+

Loading comments...

+
+ + +
+
+ + {/* Related Stories */} +
+

+ More Stories Like This +

+
+ {/* Future: Related stories based on tags */} +
+

+ Coming soon: Related stories based on tags and themes +

+
+
+
+
+
+ + +
\ No newline at end of file diff --git a/src/pages/nevertell/categories.astro b/src/pages/nevertell/categories.astro new file mode 100644 index 0000000..27f55ae --- /dev/null +++ b/src/pages/nevertell/categories.astro @@ -0,0 +1,249 @@ +--- +import { getCollection } from 'astro:content'; +import NevertellLayout from '../../layouts/NevertellLayout.astro'; + +// Get all nevertell stories to extract categories +const stories = await getCollection('nevertell'); + +// Extract all unique tags and count them +const tagCounts = {}; +stories.forEach(story => { + story.data.tags.forEach(tag => { + tagCounts[tag] = (tagCounts[tag] || 0) + 1; + }); +}); + +// Sort tags by count (descending) then alphabetically +const sortedTags = Object.entries(tagCounts) + .sort(([a, countA], [b, countB]) => { + if (countB !== countA) return countB - countA; + return a.localeCompare(b); + }); + +// Group stories by decade for timeline view +const decades = {}; +stories.forEach(story => { + story.data.tags.forEach(tag => { + if (tag.match(/^\d{4}s$/)) { // matches "1960s", "1950s", etc. + if (!decades[tag]) decades[tag] = []; + decades[tag].push(story); + } + }); +}); + +const sortedDecades = Object.entries(decades) + .sort(([a], [b]) => a.localeCompare(b)); +--- + + +
+
+ {/* Header */} +
+ + ← Back to Anonymous Stories + + +

+ 🎭 Story Categories +

+

+ Explore anonymous stories by theme, decade, and mischief level. + Every category holds its own secrets... +

+
+ + {/* Popular Categories */} + + + {/* Timeline by Decades */} + {sortedDecades.length > 0 && ( +
+

+ 📅 Stories Through the Decades +

+ +
+ {sortedDecades.map(([decade, decadeStories]) => ( + + ))} +
+
+ )} + + {/* Browse All Stories */} +
+

+ 🎪 Ready for More Mischief? +

+

+ Can't find what you're looking for? Browse all anonymous stories or share your own secret tale. +

+ +
+ + {/* Interactive Category Search with Alpine.js */} +
({ tag, count })))} + > +

+ 🔍 Find Stories by Keyword +

+ +
+ +
+ +
+ +
+ +
+
🤷‍♀️
+

No categories found matching ""

+

Try searching for: college, pranks, music, rebellion

+
+
+
+
+ + +
\ No newline at end of file diff --git a/src/pages/nevertell/index.astro b/src/pages/nevertell/index.astro new file mode 100644 index 0000000..ba079f8 --- /dev/null +++ b/src/pages/nevertell/index.astro @@ -0,0 +1,255 @@ +--- +import { getCollection } from 'astro:content'; +import NevertellLayout from '../../layouts/NevertellLayout.astro'; + +const stories = await getCollection('nevertell'); +const sortedStories = stories.sort((a, b) => b.data.publishedAt.getTime() - a.data.publishedAt.getTime()); +const allTags = [...new Set(stories.flatMap(story => story.data.tags))]; +--- + + +
+ {/* Hero Section */} +
+

+ The Stories They'll Only Whisper +

+

+ Anonymous tales of wild adventures, secret rebellions, and the kind of memories + that make you say "I can't believe I did that!" +

+

+ Mischievous wink, between you and me... +

+
+ + {/* Story Feed with Alpine.js filtering */} +
+
+

Recent Stories

+ +
+ {/* Search */} + + + {/* Filter button */} + + + + Share Your Story + +
+
+ + {/* Tag Filters */} +
+

Filter by tags:

+
+ +
+
+ +
+
+ + {/* Stories Grid */} +
+ + +
+
🤷‍♀️
+

No stories match your search

+

Try different keywords or clear your filters

+ +
+
+ + {/* Load More */} +
+ +
+
+ + {/* Categories Preview */} + +
+ + +
\ No newline at end of file diff --git a/src/styles/global.css b/src/styles/global.css new file mode 100644 index 0000000..0b471cb --- /dev/null +++ b/src/styles/global.css @@ -0,0 +1,120 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + html { + font-family: Inter, system-ui, sans-serif; + scroll-behavior: smooth; + } + + /* Accessibility improvements for seniors */ + body { + font-size: 18px; + line-height: 1.6; + min-height: 100vh; + } + + /* Larger tap targets for mobile/touch */ + button, a, input, textarea, select { + min-height: 44px; + min-width: 44px; + } + + /* High contrast mode support */ + @media (prefers-contrast: high) { + body { + background: white !important; + color: black !important; + } + + .btn-primary, .btn-nevertell, .btn-dignity { + background: black !important; + color: white !important; + border: 2px solid black !important; + } + + .story-card { + border: 2px solid black !important; + } + } + + /* Reduced motion support */ + @media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } + } + + /* Large text mode support */ + @media (min-resolution: 192dpi) { + body { + font-size: 20px; + } + + .story-content { + font-size: 1.25rem; + line-height: 1.8; + } + } + + /* Focus improvements for keyboard navigation */ + *:focus { + outline: 3px solid #3b82f6; + outline-offset: 2px; + } +} + +@layer components { + .story-card { + @apply bg-white rounded-lg shadow-md p-6 mb-6 border border-gray-200 hover:shadow-lg transition-shadow; + } + + .comment-thread { + @apply border-l-2 border-gray-200 pl-4 ml-4 mt-4; + } + + .btn-primary { + @apply px-6 py-3 rounded-lg font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2; + } + + .btn-nevertell { + @apply btn-primary bg-nevertell-primary text-white hover:bg-indigo-700 focus:ring-nevertell-primary; + } + + .btn-dignity { + @apply btn-primary bg-dignity-primary text-white hover:bg-emerald-700 focus:ring-dignity-primary; + } + + /* Story content styling */ + .story-content { + @apply text-lg leading-relaxed; + } + + .story-content p { + @apply mb-4; + } + + .story-content h2 { + @apply text-xl font-semibold mt-8 mb-4; + } + + .story-content h3 { + @apply text-lg font-semibold mt-6 mb-3; + } + + .story-content blockquote { + @apply border-l-4 border-gray-300 pl-4 italic my-6; + } + + .story-content em { + @apply italic text-gray-600; + } + + .story-content strong { + @apply font-semibold; + } +} \ No newline at end of file diff --git a/tailwind.config.mjs b/tailwind.config.mjs new file mode 100644 index 0000000..1c51bd0 --- /dev/null +++ b/tailwind.config.mjs @@ -0,0 +1,36 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], + theme: { + extend: { + colors: { + // nevertell.ink theme - playful, mischievous + nevertell: { + primary: '#6366f1', // indigo + secondary: '#ec4899', // pink + accent: '#f59e0b', // amber + background: '#fafafa', + text: '#1f2937', + }, + // dignity.ink theme - warm, respectful + dignity: { + primary: '#059669', // emerald + secondary: '#dc2626', // red + accent: '#d97706', // amber + background: '#f9fafb', + text: '#374151', + }, + }, + fontFamily: { + serif: ['Georgia', 'Times New Roman', 'serif'], + sans: ['Inter', 'system-ui', 'sans-serif'], + }, + fontSize: { + // Larger fonts for accessibility + 'story-title': ['2.5rem', { lineHeight: '1.2' }], + 'story-body': ['1.125rem', { lineHeight: '1.7' }], + }, + }, + }, + plugins: [], +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bd4a6a7 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "astro/tsconfigs/strict", + "compilerOptions": { + "types": ["alpinejs"] + } +} \ No newline at end of file diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..6e82593 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite'; + +export default defineConfig({ + server: { + host: '0.0.0.0', + port: 4321, + allowedHosts: [ + process.env.DOMAIN || 'st.l.supported.systems', + 'localhost', + '127.0.0.1' + ] + } +}); \ No newline at end of file